From 6416a752c6d08181ffe9ea623569f67bab47a7f3 Mon Sep 17 00:00:00 2001 From: Jaedan Date: Fri, 19 Jul 2024 18:15:30 -0700 Subject: [PATCH] Add a bloom example --- CMakeLists.txt | 1 + Content/Shaders/Compiled/Bloom.vert.spv | Bin 0 -> 1016 bytes .../Shaders/Compiled/BloomDownsample.frag.spv | Bin 0 -> 5468 bytes .../Shaders/Compiled/BloomUpsample.frag.spv | Bin 0 -> 3884 bytes Content/Shaders/Compiled/LerpBlend.frag.spv | Bin 0 -> 1128 bytes Content/Shaders/Source/Bloom.vert | 11 + Content/Shaders/Source/BloomDownsample.frag | 60 ++ Content/Shaders/Source/BloomUpsample.frag | 48 ++ Content/Shaders/Source/LerpBlend.frag | 18 + Examples/Bloom.c | 595 ++++++++++++++++++ SDL_gpu_examples.h | 1 + main.c | 1 + 12 files changed, 735 insertions(+) create mode 100644 Content/Shaders/Compiled/Bloom.vert.spv create mode 100644 Content/Shaders/Compiled/BloomDownsample.frag.spv create mode 100644 Content/Shaders/Compiled/BloomUpsample.frag.spv create mode 100644 Content/Shaders/Compiled/LerpBlend.frag.spv create mode 100644 Content/Shaders/Source/Bloom.vert create mode 100644 Content/Shaders/Source/BloomDownsample.frag create mode 100644 Content/Shaders/Source/BloomUpsample.frag create mode 100644 Content/Shaders/Source/LerpBlend.frag create mode 100644 Examples/Bloom.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f9cf2f4..0438209 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(SDL_gpu_examples Examples/TriangleMSAA.c Examples/Cubemap.c Examples/WindowResize.c + Examples/Bloom.c ) target_include_directories(SDL_gpu_examples PRIVATE . spirv) diff --git a/Content/Shaders/Compiled/Bloom.vert.spv b/Content/Shaders/Compiled/Bloom.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..87ce0bac0c2f26fd7fdc3a3f48f76c1747e98157 GIT binary patch literal 1016 zcmYk3OG_M45QT4MY#;IcPK=rsmx8!Z5ky4{OcpK-hHQgqBMp;jnQ1{>`P=+eZiIYa z+qd-&HC1)$+*_xr+pW!!5I%+RFd8<(+nEh5kN`GZ`Of*}`Szf^+1}mTkueq86;aM~ z7^~tT{{LO&Jrj{}Y##f9wfVL1&s<2t5WjbB0wTY-FMnqbXGPJ!0ZFR+4M06X%Pju3 z`}ZR2|H=Ag_CN~yh8tTe26>qmJzK#fJ(#QGCwH8|BkqZqb+L~;^@*t^@{GmYVTX$E z$+MSwV()oS@eMfMF)d(7tbfL52x_F2HEnD5%hd=c}gYYA_k zIL9)vTyftscX#o=+!ZW@rxW{sC-Sb^_W6eH%Naf5FCYp22-Mw7VD9CZs{1pyX1}kP zy?$}SKHq8#*I=tPrk==Kt1)|D&_ykNYwydquI5wTRd0&9Bln;VbNR1Kd4T-~0SrKK literal 0 HcmV?d00001 diff --git a/Content/Shaders/Compiled/BloomDownsample.frag.spv b/Content/Shaders/Compiled/BloomDownsample.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..ef815fc2249225c1bb3e44ebcc018d9824a65c02 GIT binary patch literal 5468 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 literal 0 HcmV?d00001 diff --git a/Content/Shaders/Compiled/BloomUpsample.frag.spv b/Content/Shaders/Compiled/BloomUpsample.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..414675b6a3beae22c754bb612c6ebfd3f8f1fe96 GIT binary patch literal 3884 zcmZ9OX>(OY5QYy~2#T`ECZd=S#RUWxKm@}U<)TJ`1VqJ!kc}%zE?$CyOC;is;EubZ zsEFH-@K^c8Dxc@v(>isgs;l3AyJx1~Gc&nqZt85yvT51OY)1BH));fMCYTIvdhRPd zeLbs=P7JPEv-Wl?W@XKJp*6F!wyXuOjrCW@O)SMOVsogIX9G|(t7h3>OlpNq>s!gq zAa@*VWzWm-d&jH8wffk$(b~XaLfE#P7jLhQP7Kw1`vqiP~ zV}}YniLd6kB|D8DsEyX@dEAw4*kcnSSoO~N|o zFD{DY?9bTm<8Z%g@vdhN)?bHrEqUvW{Z?}7xgOt+InmSrcmM0_rfv;X1?jeM8u{|xrqxQ_i9d!BMJ{}Pz>Hm~Dusmr|_*8C0Aig&Kx z)pw|m*tZn1-`@9>#=e&{_RXYmr4;*i(!B2{jfYFI?EQKGwMj>^jS_n0GT+KH^)z_A^zib1T>y`CV|{(G{5Mo7)HHy|@jtZ*x28#J95w zlh+*HQSXMDJ!A9kt7iY`r<(5|E^EFMPCoS9Rn(+<*1*Y!p0(gq&pKjjL^Py)WIMs76aam6noP6}U3G5lhx8I!S{9gZS-U4==RP$D1 z`B-NgIMv)uY>j+qz7OpB=0fxRVEZ-~nzw`HHAkx1cW6E|?*ONoA0RGkeh^MRdVL72 z=D4>#U~A;#oF4|4H9rC;A9{9zohu)Dc7g55T)8t@ zAH6yzNL*Qbcfy*>>mAK(5N zaC+Yk5L+W3diuexlj=E0EFXFXzy~n-&@%|OCv%}^2rRETQa$^L&4->5aH{7Jaam6l zPCoQJ3sz6urNdxr?E2E}^o)h3y&x^#?$cLVnz^-jB^t=qVCv&0a z6|lVKNc9{gHXnLk1*dvW5|{N%!O2IjuYuD$d5YK?`Oy42xO|3hz{$rtZ-P_JZxLG~ zADZ6=yS};5{0`W@&4uQ7!Sb3T)jUaT-hJoe-@|;HXZc+;K3i~S@c!oD#0gL%xgRPN|Ip2W2`%&{P*gE;B`3~&2jhgdd>*S;6dvMz855#ik zvFKGU>VE_`zJKFvej=8;fJMDr)c*puem7?^9sesP=LqgMu$*@yxZew|3BQZ~99QE1 Vz?^Tr`Ae94Uc{UGhZWXi{{o?N`B(q| literal 0 HcmV?d00001 diff --git a/Content/Shaders/Compiled/LerpBlend.frag.spv b/Content/Shaders/Compiled/LerpBlend.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..d1899c085bb15952ed96bc083349bf0e31f022ba GIT binary patch literal 1128 zcmZ9K%T60X5JfwfF(kahJG^X6!XlEzB1DlQkO)$o1q&oVtSxN70(;QFBK%&yDjTGn zGoD7Hv{X}lZ{4n{uAa(BV=RO>VIqu&-7s8pVFV_?Rde4uIXh`SK6RR3_V-jwhe|F~ zGZV(bC|>KgdjkV=*avJDoje8DRmd8`9}Xq3Wxe<$$Kh9o$X6>8f^ghjUT{UdrF9yBqH0%GkPp|HP+&0O1 z_m|z?&FzypD`885)uK=P+Qz&~#C7&Av3K9h(Gc5DSua-a?9|&^)c-2gZ@{}+)VJ4I z&tA;u3wT}}>(8T7S0`%R{Svz>zJ@KM_btTEzs7eQFQ={Y3h(g7?XSEdUL)TG0d=0A zEJSkO3+#+s)|{(mUhK@|;(2G}QooA#F7{_!?9Ajg`8u27{YLXST1QT; zb9Fv)i{Khwv)1PV*V((QwT)M6zOTH6d*|e`-d~smZ*J%2KO6fJ`#*{gh&zvI%vp%N z?|aO + +#include + +static SDL_GpuBuffer* VertexBuffer; +static SDL_GpuBuffer* IndexBuffer; + +static SDL_GpuSampler* Sampler; + +static SDL_GpuTexture* InputTexture; +static SDL_GpuTexture* IntermediateTextures[5]; +static SDL_GpuTexture* OutputTexture; + +static SDL_GpuGraphicsPipeline* DownsamplePipeline; +static SDL_GpuGraphicsPipeline* UpsamplePipeline; +static SDL_GpuGraphicsPipeline* BlendPipeline; + +static int w, h; + +static float Weight = 0.01f; +static float FilterRadius = 0.04f; + +static int Init(Context* context) { + /* Manually set up example for HDR rendering */ + context->Device = SDL_GpuCreateDevice(SDL_GPU_BACKEND_ALL, SDL_TRUE, SDL_FALSE); + if (context->Device == NULL) { + SDL_Log("GpuCreateDevice failed"); + return -1; + } + + int img_x, img_y, n; + float* hdrImageData = LoadImage("memorial.hdr", &img_x, &img_y, &n, 4, SDL_TRUE); + + if (hdrImageData == NULL) { + SDL_Log("Could not load HDR image data!"); + return -1; + } + + context->Window = SDL_CreateWindow(context->ExampleName, img_x, img_y, 0); + if (context->Window == NULL) { + SDL_Log("CreateWindow failed: %s", SDL_GetError()); + return -1; + } + + if (!SDL_GpuClaimWindow(context->Device, context->Window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) { + SDL_Log("GpuClaimWindow failed"); + return -1; + } + + SDL_GetWindowSizeInPixels(context->Window, &w, &h); + + /* Create the downsample pipeline */ + { + SDL_GpuShader* vertexShader = LoadShader(context->Device, "Bloom.vert", 0, 0, 0, 0); + if (vertexShader == NULL) { + SDL_Log("Failed to create vertex shader!"); + return -1; + } + + SDL_GpuShader* fragmentShader = LoadShader(context->Device, "BloomDownsample.frag", 1, 0, 0, 0); + if (fragmentShader == NULL) { + SDL_Log("Failed to create fragment shader!"); + return -1; + } + + SDL_GpuGraphicsPipelineCreateInfo pipelineCreateInfo = { + .attachmentInfo = { + .colorAttachmentCount = 1, + .colorAttachmentDescriptions = (SDL_GpuColorAttachmentDescription[]){{ + .format = SDL_GPU_TEXTUREFORMAT_R32G32B32A32_SFLOAT, + .blendState = { + .blendEnable = SDL_TRUE, + .alphaBlendOp = SDL_GPU_BLENDOP_ADD, + .colorBlendOp = SDL_GPU_BLENDOP_ADD, + .colorWriteMask = 0xF, + .srcColorBlendFactor = SDL_GPU_BLENDFACTOR_ONE, + .srcAlphaBlendFactor = SDL_GPU_BLENDFACTOR_ONE, + .dstColorBlendFactor = SDL_GPU_BLENDFACTOR_ZERO, + .dstAlphaBlendFactor = SDL_GPU_BLENDFACTOR_ZERO + } + }}, + }, + .vertexInputState = (SDL_GpuVertexInputState){ + .vertexBindingCount = 1, + .vertexBindings = (SDL_GpuVertexBinding[]){{ + .binding = 0, + .inputRate = SDL_GPU_VERTEXINPUTRATE_VERTEX, + .stepRate = 0, + .stride = sizeof(PositionTextureVertex) + }}, + .vertexAttributeCount = 2, + .vertexAttributes = (SDL_GpuVertexAttribute[]){{ + .binding = 0, + .format = SDL_GPU_VERTEXELEMENTFORMAT_VECTOR3, + .location = 0, + .offset = 0 + }, { + .binding = 0, + .format = SDL_GPU_VERTEXELEMENTFORMAT_VECTOR2, + .location = 1, + .offset = sizeof(float) * 3 + }} + }, + .multisampleState.sampleMask = 0xFFFF, + .primitiveType = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, + .vertexShader = vertexShader, + .fragmentShader = fragmentShader + }; + + SDL_GpuGraphicsPipeline* pipeline = SDL_GpuCreateGraphicsPipeline(context->Device, &pipelineCreateInfo); + if (pipeline == NULL) { + SDL_Log("Failed to create pipeline!"); + return -1; + } + + SDL_GpuReleaseShader(context->Device, vertexShader); + SDL_GpuReleaseShader(context->Device, fragmentShader); + + DownsamplePipeline = pipeline; + } + + /* Create the upsample pipeline */ + { + SDL_GpuShader* vertexShader = LoadShader(context->Device, "Bloom.vert", 0, 0, 0, 0); + if (vertexShader == NULL) { + SDL_Log("Failed to create vertex shader!"); + return -1; + } + + SDL_GpuShader* fragmentShader = LoadShader(context->Device, "BloomUpsample.frag", 1, 1, 0, 0); + if (fragmentShader == NULL) { + SDL_Log("Failed to create fragment shader!"); + return -1; + } + + /* Note that this has additive blending between source and destination */ + SDL_GpuGraphicsPipelineCreateInfo pipelineCreateInfo = { + .attachmentInfo = { + .colorAttachmentCount = 1, + .colorAttachmentDescriptions = (SDL_GpuColorAttachmentDescription[]){{ + .format = SDL_GPU_TEXTUREFORMAT_R32G32B32A32_SFLOAT, + .blendState = { + .blendEnable = SDL_TRUE, + .alphaBlendOp = SDL_GPU_BLENDOP_ADD, + .colorBlendOp = SDL_GPU_BLENDOP_ADD, + .colorWriteMask = 0xF, + .srcColorBlendFactor = SDL_GPU_BLENDFACTOR_ONE, + .srcAlphaBlendFactor = SDL_GPU_BLENDFACTOR_ONE, + .dstColorBlendFactor = SDL_GPU_BLENDFACTOR_ONE, + .dstAlphaBlendFactor = SDL_GPU_BLENDFACTOR_ONE + } + }}, + }, + .vertexInputState = (SDL_GpuVertexInputState){ + .vertexBindingCount = 1, + .vertexBindings = (SDL_GpuVertexBinding[]){{ + .binding = 0, + .inputRate = SDL_GPU_VERTEXINPUTRATE_VERTEX, + .stepRate = 0, + .stride = sizeof(PositionTextureVertex) + }}, + .vertexAttributeCount = 2, + .vertexAttributes = (SDL_GpuVertexAttribute[]){{ + .binding = 0, + .format = SDL_GPU_VERTEXELEMENTFORMAT_VECTOR3, + .location = 0, + .offset = 0 + }, { + .binding = 0, + .format = SDL_GPU_VERTEXELEMENTFORMAT_VECTOR2, + .location = 1, + .offset = sizeof(float) * 3 + }} + }, + .multisampleState.sampleMask = 0xFFFF, + .primitiveType = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, + .vertexShader = vertexShader, + .fragmentShader = fragmentShader + }; + + SDL_GpuGraphicsPipeline* pipeline = SDL_GpuCreateGraphicsPipeline(context->Device, &pipelineCreateInfo); + if (pipeline == NULL) { + SDL_Log("Failed to create pipeline!"); + return -1; + } + + SDL_GpuReleaseShader(context->Device, vertexShader); + SDL_GpuReleaseShader(context->Device, fragmentShader); + + UpsamplePipeline = pipeline; + } + + /* Create the blend pipeline */ + { + SDL_GpuShader* vertexShader = LoadShader(context->Device, "Bloom.vert", 0, 0, 0, 0); + if (vertexShader == NULL) { + SDL_Log("Failed to create vertex shader!"); + return -1; + } + + SDL_GpuShader* fragmentShader = LoadShader(context->Device, "LerpBlend.frag", 2, 1, 0, 0); + if (fragmentShader == NULL) { + SDL_Log("Failed to create fragment shader!"); + return -1; + } + + SDL_GpuGraphicsPipelineCreateInfo pipelineCreateInfo = { + .attachmentInfo = { + .colorAttachmentCount = 1, + .colorAttachmentDescriptions = (SDL_GpuColorAttachmentDescription[]){{ + .format = SDL_GPU_TEXTUREFORMAT_R32G32B32A32_SFLOAT, + .blendState = { + .blendEnable = SDL_TRUE, + .alphaBlendOp = SDL_GPU_BLENDOP_ADD, + .colorBlendOp = SDL_GPU_BLENDOP_ADD, + .colorWriteMask = 0xF, + .srcColorBlendFactor = SDL_GPU_BLENDFACTOR_ONE, + .srcAlphaBlendFactor = SDL_GPU_BLENDFACTOR_ONE, + .dstColorBlendFactor = SDL_GPU_BLENDFACTOR_ZERO, + .dstAlphaBlendFactor = SDL_GPU_BLENDFACTOR_ZERO + } + }}, + }, + .vertexInputState = (SDL_GpuVertexInputState){ + .vertexBindingCount = 1, + .vertexBindings = (SDL_GpuVertexBinding[]){{ + .binding = 0, + .inputRate = SDL_GPU_VERTEXINPUTRATE_VERTEX, + .stepRate = 0, + .stride = sizeof(PositionTextureVertex) + }}, + .vertexAttributeCount = 2, + .vertexAttributes = (SDL_GpuVertexAttribute[]){{ + .binding = 0, + .format = SDL_GPU_VERTEXELEMENTFORMAT_VECTOR3, + .location = 0, + .offset = 0 + }, { + .binding = 0, + .format = SDL_GPU_VERTEXELEMENTFORMAT_VECTOR2, + .location = 1, + .offset = sizeof(float) * 3 + }} + }, + .multisampleState.sampleMask = 0xFFFF, + .primitiveType = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, + .vertexShader = vertexShader, + .fragmentShader = fragmentShader + }; + + SDL_GpuGraphicsPipeline* pipeline = SDL_GpuCreateGraphicsPipeline(context->Device, &pipelineCreateInfo); + if (pipeline == NULL) { + SDL_Log("Failed to create pipeline!"); + return -1; + } + + SDL_GpuReleaseShader(context->Device, vertexShader); + SDL_GpuReleaseShader(context->Device, fragmentShader); + + BlendPipeline = pipeline; + } + + Sampler = SDL_GpuCreateSampler(context->Device, &(SDL_GpuSamplerCreateInfo){ + .minFilter = SDL_GPU_FILTER_LINEAR, + .magFilter = SDL_GPU_FILTER_LINEAR, + .mipmapMode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR, + .addressModeU = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, + .addressModeV = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, + .addressModeW = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, + }); + + // Create the GPU resources + VertexBuffer = SDL_GpuCreateBuffer( + context->Device, + SDL_GPU_BUFFERUSAGE_VERTEX_BIT, + sizeof(PositionTextureVertex) * 4 + ); + SDL_GpuSetBufferName( + context->Device, + VertexBuffer, + "Bloom Vertex Buffer" + ); + + IndexBuffer = SDL_GpuCreateBuffer( + context->Device, + SDL_GPU_BUFFERUSAGE_INDEX_BIT, + sizeof(Uint16) * 6 + ); + + InputTexture = SDL_GpuCreateTexture(context->Device, &(SDL_GpuTextureCreateInfo){ + .format = SDL_GPU_TEXTUREFORMAT_R32G32B32A32_SFLOAT, + .width = img_x, + .height = img_y, + .depth = 1, + .layerCount = 1, + .levelCount = 1, + .usageFlags = SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT + }); + SDL_GpuSetTextureName( + context->Device, + InputTexture, + "Input Texture" + ); + + int mipSizeX = img_x; + int mipSizeY = img_y; + for (int i = 0; i < SDL_arraysize(IntermediateTextures); i++) { + mipSizeX /= 2; + mipSizeY /= 2; + + IntermediateTextures[i] = SDL_GpuCreateTexture(context->Device, &(SDL_GpuTextureCreateInfo){ + .format = SDL_GPU_TEXTUREFORMAT_R32G32B32A32_SFLOAT, + .width = mipSizeX, + .height = mipSizeY, + .depth = 1, + .layerCount = 1, + .levelCount = 1, + .usageFlags = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT | SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT + }); + } + + OutputTexture = SDL_GpuCreateTexture(context->Device, &(SDL_GpuTextureCreateInfo){ + .format = SDL_GPU_TEXTUREFORMAT_R32G32B32A32_SFLOAT, + .width = img_x, + .height = img_y, + .depth = 1, + .layerCount = 1, + .levelCount = 1, + .usageFlags = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT | SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT + }); + SDL_GpuSetTextureName( + context->Device, + OutputTexture, + "Output Texture" + ); + + // Set up buffer data + SDL_GpuTransferBuffer* bufferTransferBuffer = SDL_GpuCreateTransferBuffer( + context->Device, + SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, + (sizeof(PositionTextureVertex) * 4) + (sizeof(Uint16) * 6) + ); + + PositionTextureVertex* transferData; + SDL_GpuMapTransferBuffer( + context->Device, + bufferTransferBuffer, + SDL_FALSE, + (void**)&transferData + ); + + transferData[0] = (PositionTextureVertex){ -1, 1, 0, 0, 0 }; + transferData[1] = (PositionTextureVertex){ 1, 1, 0, 1, 0 }; + transferData[2] = (PositionTextureVertex){ 1, -1, 0, 1, 1 }; + transferData[3] = (PositionTextureVertex){ -1, -1, 0, 0, 1 }; + + Uint16* indexData = (Uint16*)&transferData[4]; + indexData[0] = 0; + indexData[1] = 1; + indexData[2] = 2; + indexData[3] = 0; + indexData[4] = 2; + indexData[5] = 3; + + SDL_GpuUnmapTransferBuffer(context->Device, bufferTransferBuffer); + + // Set up texture data + SDL_GpuTransferBuffer* imageDataTransferBuffer = SDL_GpuCreateTransferBuffer( + context->Device, + SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, + sizeof(float) * 4 * img_x * img_y + ); + + SDL_GpuSetTransferData( + context->Device, + hdrImageData, + &(SDL_GpuTransferBufferRegion) { + .transferBuffer = imageDataTransferBuffer, + .offset = 0, + .size = sizeof(float) * 4 * img_x * img_y + }, + SDL_FALSE + ); + + SDL_free(hdrImageData); + + // Upload the transfer data to the GPU resources + SDL_GpuCommandBuffer* uploadCmdBuf = SDL_GpuAcquireCommandBuffer(context->Device); + SDL_GpuCopyPass* copyPass = SDL_GpuBeginCopyPass(uploadCmdBuf); + + SDL_GpuUploadToBuffer( + copyPass, + &(SDL_GpuTransferBufferLocation) { + .transferBuffer = bufferTransferBuffer, + .offset = 0 + }, + & (SDL_GpuBufferRegion) { + .buffer = VertexBuffer, + .offset = 0, + .size = sizeof(PositionTextureVertex) * 4 + }, + SDL_FALSE + ); + + SDL_GpuUploadToBuffer( + copyPass, + &(SDL_GpuTransferBufferLocation) { + .transferBuffer = bufferTransferBuffer, + .offset = sizeof(PositionTextureVertex) * 4 + }, + & (SDL_GpuBufferRegion) { + .buffer = IndexBuffer, + .offset = 0, + .size = sizeof(Uint16) * 6 + }, + SDL_FALSE + ); + + SDL_GpuUploadToTexture( + copyPass, + &(SDL_GpuTextureTransferInfo) { + .transferBuffer = imageDataTransferBuffer, + .offset = 0, /* Zeroes out the rest */ + }, + & (SDL_GpuTextureRegion) { + .textureSlice.texture = InputTexture, + .w = img_x, + .h = img_y, + .d = 1 + }, + SDL_FALSE + ); + + SDL_GpuEndCopyPass(copyPass); + SDL_GpuSubmit(uploadCmdBuf); + SDL_GpuReleaseTransferBuffer(context->Device, bufferTransferBuffer); + SDL_GpuReleaseTransferBuffer(context->Device, imageDataTransferBuffer); + + SDL_Log("Press Left/Right to increase/decrease the blur radius (Current: %f)", FilterRadius); + SDL_Log("Press Up/Down to increase/decrease the final blend weight (Current: %f)", Weight); + + return 0; +} + +static int Update(Context* context) { + if (context->LeftPressed) { + FilterRadius -= 0.01f; + if (FilterRadius < 0.01f) { + FilterRadius = 0.01f; + } + SDL_Log("Blur Radius: %f", FilterRadius); + } else if (context->RightPressed) { + FilterRadius += 0.01f; + SDL_Log("Blur Radius: %f", FilterRadius); + } + + if (context->UpPressed) { + Weight += 0.001f; + SDL_Log("Blend Weight: %f", Weight); + } else if (context->DownPressed) { + Weight -= 0.001f; + if (Weight < 0) { + Weight = 0; + } + SDL_Log("Blend Weight: %f", Weight); + } + + return 0; +} + +static int Draw(Context* context) { + SDL_GpuCommandBuffer* cmdbuf = SDL_GpuAcquireCommandBuffer(context->Device); + if (cmdbuf == NULL) { + SDL_Log("GpuAcquireCommandBuffer failed"); + return -1; + } + + Uint32 w, h; + SDL_GpuTexture* swapchainTexture = SDL_GpuAcquireSwapchainTexture(cmdbuf, context->Window, &w, &h); + if (swapchainTexture == NULL) { + SDL_GpuSubmit(cmdbuf); + return 0; + } + + /* Down sample the original texture for each layer */ + SDL_GpuTexture* sourceTexture = InputTexture; + for (int i = 0; i < SDL_arraysize(IntermediateTextures); i++) { + + SDL_GpuColorAttachmentInfo colorAttachmentInfo = { 0 }; + colorAttachmentInfo.textureSlice.texture = IntermediateTextures[i]; + colorAttachmentInfo.clearColor = (SDL_GpuColor){ 0.0f, 0.0f, 0.0f, 1.0f }; + colorAttachmentInfo.loadOp = SDL_GPU_LOADOP_CLEAR; + colorAttachmentInfo.storeOp = SDL_GPU_STOREOP_STORE; + + SDL_GpuRenderPass* renderPass = SDL_GpuBeginRenderPass(cmdbuf, &colorAttachmentInfo, 1, NULL); + + SDL_GpuBindGraphicsPipeline(renderPass, DownsamplePipeline); + SDL_GpuBindVertexBuffers(renderPass, 0, &(SDL_GpuBufferBinding){.buffer = VertexBuffer, .offset = 0 }, 1); + SDL_GpuBindIndexBuffer(renderPass, &(SDL_GpuBufferBinding){.buffer = IndexBuffer, .offset = 0 }, SDL_GPU_INDEXELEMENTSIZE_16BIT); + SDL_GpuBindFragmentSamplers(renderPass, 0, &(SDL_GpuTextureSamplerBinding){.texture = sourceTexture, .sampler = Sampler }, 1); + SDL_GpuDrawIndexedPrimitives(renderPass, 0, 0, 2, 1); + + SDL_GpuEndRenderPass(renderPass); + + sourceTexture = IntermediateTextures[i]; + } + + /* Up-sample in reverse, blending with the previous texture */ + for (int i = SDL_arraysize(IntermediateTextures) - 1; i > 0; i--) { + SDL_GpuColorAttachmentInfo colorAttachmentInfo = { 0 }; + colorAttachmentInfo.textureSlice.texture = IntermediateTextures[i - 1]; + colorAttachmentInfo.clearColor = (SDL_GpuColor){ 0.0f, 0.0f, 0.0f, 1.0f }; + colorAttachmentInfo.loadOp = SDL_GPU_LOADOP_LOAD; + colorAttachmentInfo.storeOp = SDL_GPU_STOREOP_STORE; + + SDL_GpuRenderPass* renderPass = SDL_GpuBeginRenderPass(cmdbuf, &colorAttachmentInfo, 1, NULL); + + SDL_GpuBindGraphicsPipeline(renderPass, UpsamplePipeline); + SDL_GpuBindVertexBuffers(renderPass, 0, &(SDL_GpuBufferBinding){.buffer = VertexBuffer, .offset = 0 }, 1); + SDL_GpuBindIndexBuffer(renderPass, &(SDL_GpuBufferBinding){.buffer = IndexBuffer, .offset = 0 }, SDL_GPU_INDEXELEMENTSIZE_16BIT); + SDL_GpuBindFragmentSamplers(renderPass, 0, &(SDL_GpuTextureSamplerBinding){.texture = IntermediateTextures[i], .sampler = Sampler }, 1); + SDL_GpuPushFragmentUniformData(cmdbuf, 0, &FilterRadius, sizeof(FilterRadius)); + SDL_GpuDrawIndexedPrimitives(renderPass, 0, 0, 2, 1); + + SDL_GpuEndRenderPass(renderPass); + } + + /* Blend the final texture into the original texture */ + { + SDL_GpuColorAttachmentInfo colorAttachmentInfo = { 0 }; + colorAttachmentInfo.textureSlice.texture = OutputTexture; + colorAttachmentInfo.clearColor = (SDL_GpuColor){ 0.0f, 0.0f, 0.0f, 1.0f }; + colorAttachmentInfo.loadOp = SDL_GPU_LOADOP_CLEAR; + colorAttachmentInfo.storeOp = SDL_GPU_STOREOP_STORE; + + SDL_GpuRenderPass* renderPass = SDL_GpuBeginRenderPass(cmdbuf, &colorAttachmentInfo, 1, NULL); + + SDL_GpuBindGraphicsPipeline(renderPass, BlendPipeline); + SDL_GpuBindVertexBuffers(renderPass, 0, &(SDL_GpuBufferBinding){.buffer = VertexBuffer, .offset = 0 }, 1); + SDL_GpuBindIndexBuffer(renderPass, &(SDL_GpuBufferBinding){.buffer = IndexBuffer, .offset = 0 }, SDL_GPU_INDEXELEMENTSIZE_16BIT); + SDL_GpuBindFragmentSamplers(renderPass, 0, + &(SDL_GpuTextureSamplerBinding[]){ + { .texture = InputTexture, .sampler = Sampler }, + { .texture = IntermediateTextures[0], .sampler = Sampler } + }, 2); + SDL_GpuPushFragmentUniformData(cmdbuf, 0, &Weight, sizeof(Weight)); + SDL_GpuDrawIndexedPrimitives(renderPass, 0, 0, 2, 1); + + SDL_GpuEndRenderPass(renderPass); + } + + /* Finally, blit the output directly to the swapchain texture. In a real render pipeline, it would be used as the input + * to a tonemapping pass. */ + SDL_GpuBlit( + cmdbuf, + &(SDL_GpuTextureRegion){ + .textureSlice.texture = OutputTexture, + .w = w, + .h = h, + .d = 1 + }, + & (SDL_GpuTextureRegion) { + .textureSlice.texture = swapchainTexture, + .w = w, + .h = h, + .d = 1 + }, + SDL_GPU_FILTER_LINEAR, + SDL_FALSE + ); + + SDL_GpuSubmit(cmdbuf); + + return 0; +} + +static void Quit(Context* context) { + SDL_GpuReleaseBuffer(context->Device, VertexBuffer); + SDL_GpuReleaseBuffer(context->Device, IndexBuffer); + SDL_GpuReleaseSampler(context->Device, Sampler); + + SDL_GpuReleaseGraphicsPipeline(context->Device, DownsamplePipeline); + SDL_GpuReleaseGraphicsPipeline(context->Device, UpsamplePipeline); + SDL_GpuReleaseGraphicsPipeline(context->Device, BlendPipeline); + + SDL_GpuReleaseTexture(context->Device, InputTexture); + for (int i = 0; i < SDL_arraysize(IntermediateTextures); i++) { + SDL_GpuReleaseTexture(context->Device, IntermediateTextures[i]); + } + SDL_GpuReleaseTexture(context->Device, OutputTexture); + + CommonQuit(context); +} + +Example Bloom_Example = { "Bloom", Init, Update, Draw, Quit }; diff --git a/SDL_gpu_examples.h b/SDL_gpu_examples.h index 1f6411a..afb1ed4 100644 --- a/SDL_gpu_examples.h +++ b/SDL_gpu_examples.h @@ -109,5 +109,6 @@ extern Example Texture2DArray_Example; extern Example TriangleMSAA_Example; extern Example Cubemap_Example; extern Example WindowResize_Example; +extern Example Bloom_Example; #endif diff --git a/main.c b/main.c index 48ed831..4d74c62 100644 --- a/main.c +++ b/main.c @@ -23,6 +23,7 @@ static Example* Examples[] = &TriangleMSAA_Example, &Cubemap_Example, &WindowResize_Example, + &Bloom_Example }; int main(int argc, char **argv)