diff --git a/private/sdl_gpu_test/6_ui/app.cpp b/private/sdl_gpu_test/6_ui/app.cpp index 63865de..46d79d9 100644 --- a/private/sdl_gpu_test/6_ui/app.cpp +++ b/private/sdl_gpu_test/6_ui/app.cpp @@ -9,9 +9,7 @@ #include "../sdlpp/keyboard.hpp" #include "../util/bitmap.hpp" -#include "../util/font_map.hpp" #include "../util/mesh.hpp" -#include "../util/texture_atlas.hpp" namespace sdl_gpu_test { @@ -26,11 +24,6 @@ struct VertexShaderParameters glm::mat4 worldToView; glm::mat4 viewToClip; }; - -struct UIVertexShaderParameters -{ - glm::vec2 windowSize; -}; } void UIApp::init(const AppInitArgs& args) @@ -49,7 +42,7 @@ void UIApp::init(const AppInitArgs& args) createMeshPipeline(); // create UI pipeline - createUIPipeline(); + mUIRenderer.init(*this); // load the mesh const Mesh mesh = loadMesh(mFileSystem.getPath("meshes/cube.obj")); @@ -70,57 +63,6 @@ void UIApp::init(const AppInitArgs& args) mTexture = loadTexture("bitmaps/cube.png", textureArgs); mSampler.create(mDevice, {}); - const FontMap fontMap = loadFontMap(mFileSystem.getPath("fonts/symtext.fnt")); - const UVFontMap uvFontMap = makeUVFontMap({ - .original = &fontMap, - .textureWidth = 256, - .textureHeight = 256 - }); - - unsigned posX = 100; - const unsigned posY = 100; - for (const char chr : std::string_view("Dies ist ein Test-Text!")) - { - const UVFontMapEntry& entry = uvFontMap.entries[chr < 0 ? '_' : chr]; - const UIVertex topLeft = { - .pos = {posX + entry.xOffset, posY + entry.yOffset}, - .texcoord = {entry.uvX, entry.uvY} - }; - const UIVertex bottomRight = { - .pos = {topLeft.pos.x + static_cast(entry.width), topLeft.pos.y + static_cast(entry.height)}, - .texcoord = {entry.uvX + entry.uvWidth, entry.uvY + entry.uvHeight} - }; - const UIVertex bottomLeft = { - .pos = {topLeft.pos.x, bottomRight.pos.y}, - .texcoord = {topLeft.texcoord.x, bottomRight.texcoord.y} - }; - const UIVertex topRight = { - .pos = {bottomRight.pos.x, topLeft.pos.y}, - .texcoord = {bottomRight.texcoord.x, topLeft.texcoord.y} - }; - - mUIVertices.push_back(topLeft); - mUIVertices.push_back(bottomLeft); - mUIVertices.push_back(topRight); - mUIVertices.push_back(bottomRight); - mUIVertices.push_back(topRight); - mUIVertices.push_back(bottomLeft); - - posX += entry.xAdvance; - } - - // create UI vertex buffer - mUIVertexBuffer.create(mDevice, { - .usage = {.vertex = true}, - .size = static_cast(mUIVertices.size() * sizeof(UIVertex)) - }); - uploadVertexData(mUIVertexBuffer, std::span(mUIVertices)); - - // and UI texture - mUITexture = loadTexture("bitmaps/ui.png", textureArgs); - - mUISampler.create(mDevice, {}); - // open gamepad const std::vector gamepads = sdlpp::getGamepads(); if (!gamepads.empty()) @@ -188,25 +130,12 @@ void UIApp::update(const AppUpdateArgs& args) renderPass.end(); // render the "UI" - if (!mUIVertices.empty()) - { - const UIVertexShaderParameters uiVertexShaderParameters = { - .windowSize = { - static_cast(swapchainWidth), static_cast(swapchainHeight) - } - }; - colorTargets[0].loadOp = sdlpp::GPULoadOp::LOAD; // don't clear again - renderPass = cmdBuffer.beginRenderPass({ - .colorTargetInfos = colorTargets - }); - cmdBuffer.pushFragmentUniformData(0, std::span(&WHITE, 1)); - cmdBuffer.pushVertexUniformData(0, std::span(&uiVertexShaderParameters, 1)); - renderPass.bindFragmentSampler({.texture = mUITexture, .sampler = mUISampler}); - renderPass.bindGraphicsPipeline(mUIPipeline); - renderPass.bindVertexBuffer({.buffer = mUIVertexBuffer}); - renderPass.drawPrimitives({.numVertices = static_cast(mUIVertices.size())}); - renderPass.end(); - } + mUIRenderer.render({ + .cmdBuffer = &cmdBuffer, + .targetTexture = &swapchainTexture, + .targetTextureWidth = swapchainWidth, + .targetTextureHeight = swapchainHeight + }); // finalize cmdBuffer.submit(); @@ -300,67 +229,6 @@ void UIApp::createMeshPipeline() }); } -void UIApp::createUIPipeline() -{ - // create shaders - const sdlpp::GPUShader vertexShader = loadShader("shaders/glsl/ui.vert.spv", { - .format = sdlpp::GPUShaderFormat::SPIRV, - .stage = sdlpp::GPUShaderStage::VERTEX, - .numUniformBuffers = 1 - }); - const sdlpp::GPUShader fragmentShader = loadShader("shaders/glsl/color_from_texture.frag.spv", { - .format = sdlpp::GPUShaderFormat::SPIRV, - .stage = sdlpp::GPUShaderStage::FRAGMENT, - .numSamplers = 1, - .numUniformBuffers = 1 - }); - std::array colorTargetsDescs = { - sdlpp::GPUColorTargetDescription{ - .format = mDevice.getSwapchainTextureFormat(mWindow), - .blendState = { - .enableBlend = true, - .srcColorBlendfactor = sdlpp::GPUBlendFactor::SRC_ALPHA, - .dstColorBlendfactor = sdlpp::GPUBlendFactor::ONE_MINUS_SRC_ALPHA, - .colorBlendOp = sdlpp::GPUBlendOp::ADD, - .srcAlphaBlendfactor = sdlpp::GPUBlendFactor::ONE, - .dstAlphaBlendfactor = sdlpp::GPUBlendFactor::ZERO, - .alphaBlendOp = sdlpp::GPUBlendOp::ADD - } - } - }; - std::array vertexBindings = { - sdlpp::GPUVertexBinding{ - .index = 0, - .pitch = sizeof(UIVertex) - } - }; - std::array vertexAttributes = { - sdlpp::GPUVertexAttribute{ - .location = 0, - .bindingIndex = 0, - .format = sdlpp::GPUVertexElementFormat::FLOAT2, - .offset = offsetof(UIVertex, pos) - }, - sdlpp::GPUVertexAttribute{ - .location = 1, - .bindingIndex = 0, - .format = sdlpp::GPUVertexElementFormat::FLOAT2, - .offset = offsetof(UIVertex, texcoord) - } - }; - mUIPipeline.create(mDevice, { - .vertexShader = vertexShader, - .fragmentShader = fragmentShader, - .vertexInputState = { - .vertexBindings = vertexBindings, - .vertexAttributes = vertexAttributes - }, - .targetInfo = { - .colorTargetDescriptions = colorTargetsDescs - } - }); -} - void UIApp::processInput(const AppUpdateArgs& args) { const std::span keystates = sdlpp::getKeyboardState(); diff --git a/private/sdl_gpu_test/6_ui/app.hpp b/private/sdl_gpu_test/6_ui/app.hpp index 299d531..6f6c6dd 100644 --- a/private/sdl_gpu_test/6_ui/app.hpp +++ b/private/sdl_gpu_test/6_ui/app.hpp @@ -6,17 +6,12 @@ #include +#include "./ui/ui_renderer.hpp" #include "../application.hpp" #include "../sdlpp/gamepad.hpp" namespace sdl_gpu_test { -struct UIVertex -{ - glm::vec2 pos; - glm::vec2 texcoord; -}; - class UIApp : public Application { private: @@ -26,11 +21,7 @@ private: sdlpp::GPUTexture mTexture; sdlpp::GPUSampler mSampler; - sdlpp::GPUBuffer mUIVertexBuffer; - sdlpp::GPUGraphicsPipeline mUIPipeline; - std::vector mUIVertices; - sdlpp::GPUTexture mUITexture; - sdlpp::GPUSampler mUISampler; + UIRenderer mUIRenderer; sdlpp::Gamepad mGamepad; Uint32 mNumVertices = 0; @@ -45,7 +36,6 @@ public: void handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event) override; private: void createMeshPipeline(); - void createUIPipeline(); void processInput(const AppUpdateArgs& args); }; } // namespace sdl_gpu_test diff --git a/private/sdl_gpu_test/6_ui/ui/ui_renderer.cpp b/private/sdl_gpu_test/6_ui/ui/ui_renderer.cpp new file mode 100644 index 0000000..ee3a4bb --- /dev/null +++ b/private/sdl_gpu_test/6_ui/ui/ui_renderer.cpp @@ -0,0 +1,167 @@ + +#include "./ui_renderer.hpp" + +#include + +namespace sdl_gpu_test::inline app6 +{ +namespace +{ +struct UIVertexShaderParameters +{ + glm::vec2 windowSize; +}; +} + +void UIRenderer::init(Application& application) +{ + // for convenience + mApplication = &application; + const sdlpp::GPUDevice& device = application.getDevice(); + + // create shaders + const sdlpp::GPUShader vertexShader = application.loadShader("shaders/glsl/ui.vert.spv", { + .format = sdlpp::GPUShaderFormat::SPIRV, + .stage = sdlpp::GPUShaderStage::VERTEX, + .numUniformBuffers = 1 + }); + const sdlpp::GPUShader fragmentShader = application.loadShader("shaders/glsl/color_from_texture.frag.spv", { + .format = sdlpp::GPUShaderFormat::SPIRV, + .stage = sdlpp::GPUShaderStage::FRAGMENT, + .numSamplers = 1, + .numUniformBuffers = 1 + }); + + std::array colorTargetsDescs = { + sdlpp::GPUColorTargetDescription{ + .format = device.getSwapchainTextureFormat(application.getWindow()), + .blendState = { + .enableBlend = true, + .srcColorBlendfactor = sdlpp::GPUBlendFactor::SRC_ALPHA, + .dstColorBlendfactor = sdlpp::GPUBlendFactor::ONE_MINUS_SRC_ALPHA, + .colorBlendOp = sdlpp::GPUBlendOp::ADD, + .srcAlphaBlendfactor = sdlpp::GPUBlendFactor::ONE, + .dstAlphaBlendfactor = sdlpp::GPUBlendFactor::ZERO, + .alphaBlendOp = sdlpp::GPUBlendOp::ADD + } + } + }; + std::array vertexBindings = { + sdlpp::GPUVertexBinding{ + .index = 0, + .pitch = sizeof(UIVertex) + } + }; + std::array vertexAttributes = { + sdlpp::GPUVertexAttribute{ + .location = 0, + .bindingIndex = 0, + .format = sdlpp::GPUVertexElementFormat::FLOAT2, + .offset = offsetof(UIVertex, pos) + }, + sdlpp::GPUVertexAttribute{ + .location = 1, + .bindingIndex = 0, + .format = sdlpp::GPUVertexElementFormat::FLOAT2, + .offset = offsetof(UIVertex, texcoord) + } + }; + mUIPipeline.create(device, { + .vertexShader = vertexShader, + .fragmentShader = fragmentShader, + .vertexInputState = { + .vertexBindings = vertexBindings, + .vertexAttributes = vertexAttributes + }, + .targetInfo = { + .colorTargetDescriptions = colorTargetsDescs + } + }); + + + const FontMap fontMap = loadFontMap(application.getFileSystem().getPath("fonts/symtext.fnt")); + const UVFontMap uvFontMap = makeUVFontMap({ + .original = &fontMap, + .textureWidth = 256, + .textureHeight = 256 + }); + + unsigned posX = 100; + const unsigned posY = 100; + for (const char chr : std::string_view("Dies ist ein Test-Text!")) + { + const UVFontMapEntry& entry = uvFontMap.entries[chr < 0 ? '_' : chr]; + const UIVertex topLeft = { + .pos = {posX + entry.xOffset, posY + entry.yOffset}, + .texcoord = {entry.uvX, entry.uvY} + }; + const UIVertex bottomRight = { + .pos = {topLeft.pos.x + static_cast(entry.width), topLeft.pos.y + static_cast(entry.height)}, + .texcoord = {entry.uvX + entry.uvWidth, entry.uvY + entry.uvHeight} + }; + const UIVertex bottomLeft = { + .pos = {topLeft.pos.x, bottomRight.pos.y}, + .texcoord = {topLeft.texcoord.x, bottomRight.texcoord.y} + }; + const UIVertex topRight = { + .pos = {bottomRight.pos.x, topLeft.pos.y}, + .texcoord = {bottomRight.texcoord.x, topLeft.texcoord.y} + }; + + mUIVertices.push_back(topLeft); + mUIVertices.push_back(bottomLeft); + mUIVertices.push_back(topRight); + mUIVertices.push_back(bottomRight); + mUIVertices.push_back(topRight); + mUIVertices.push_back(bottomLeft); + + posX += entry.xAdvance; + } + + // create UI vertex buffer + mUIVertexBuffer.create(device, { + .usage = {.vertex = true}, + .size = static_cast(mUIVertices.size() * sizeof(UIVertex)) + }); + application.uploadVertexData(mUIVertexBuffer, std::span(mUIVertices)); + + // create texture and sampler + sdlpp::GPUTextureCreateArgs textureArgs = { + .format = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM_SRGB, + .usage = {.sampler = true} + }; + mUITexture = application.loadTexture("bitmaps/ui.png", textureArgs); + + mUISampler.create(device, {}); +} + +void UIRenderer::render(const UIRendererRenderArgs& args) +{ + if (!mUIVertices.empty()) + { + const UIVertexShaderParameters uiVertexShaderParameters = { + .windowSize = { + static_cast(args.targetTextureWidth), static_cast(args.targetTextureHeight) + } + }; + std::array colorTargets = {sdlpp::GPUColorTargetInfo{ + .texture = *args.targetTexture, + .clearColor = {.r = 0.f, .g = 0.f, .b = 0.f, .a = 1.f}, + .loadOp = sdlpp::GPULoadOp::LOAD, + }}; + colorTargets[0].loadOp = sdlpp::GPULoadOp::LOAD; // don't clear again + sdlpp::GPURenderPass renderPass = args.cmdBuffer->beginRenderPass({ + .colorTargetInfos = colorTargets + }); + static const glm::vec4 WHITE(1.f, 1.f, 1.f, 1.f); + args.cmdBuffer->pushFragmentUniformData(0, std::span(&WHITE, 1)); + args.cmdBuffer->pushVertexUniformData(0, std::span(&uiVertexShaderParameters, 1)); + renderPass.bindFragmentSampler({.texture = mUITexture, .sampler = mUISampler}); + renderPass.bindGraphicsPipeline(mUIPipeline); + renderPass.bindVertexBuffer({.buffer = mUIVertexBuffer}); + renderPass.drawPrimitives({.numVertices = static_cast(mUIVertices.size())}); + renderPass.end(); + } +} + +} diff --git a/private/sdl_gpu_test/6_ui/ui/ui_renderer.hpp b/private/sdl_gpu_test/6_ui/ui/ui_renderer.hpp new file mode 100644 index 0000000..d304b8f --- /dev/null +++ b/private/sdl_gpu_test/6_ui/ui/ui_renderer.hpp @@ -0,0 +1,44 @@ + +#pragma once + +#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_6_UI_UI_UI_RENDERER_HPP_INCLUDED) +#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_6_UI_UI_UI_RENDERER_HPP_INCLUDED 1 + +#include +#include "../../application.hpp" +#include "../../sdlpp/gpu.hpp" +#include "../../util/font_map.hpp" + +namespace sdl_gpu_test::inline app6 +{ +struct UIVertex +{ + glm::vec2 pos; + glm::vec2 texcoord; +}; + +struct UIRendererRenderArgs +{ + const sdlpp::GPUCommandBuffer* cmdBuffer; + const sdlpp::GPUTexture* targetTexture; + unsigned targetTextureWidth; + unsigned targetTextureHeight; +}; + +class UIRenderer +{ +private: + Application* mApplication = nullptr; + sdlpp::GPUBuffer mUIVertexBuffer; + sdlpp::GPUGraphicsPipeline mUIPipeline; + std::vector mUIVertices; + sdlpp::GPUTexture mUITexture; + sdlpp::GPUSampler mUISampler; + UVFontMap mFontMap; +public: + void init(Application& application); + void render(const UIRendererRenderArgs& args); +}; +} // namespace sdl_gpu_test::inline app6 + +#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_6_UI_UI_UI_RENDERER_HPP_INCLUDED) diff --git a/private/sdl_gpu_test/SModule b/private/sdl_gpu_test/SModule index 5992680..7568632 100644 --- a/private/sdl_gpu_test/SModule +++ b/private/sdl_gpu_test/SModule @@ -16,6 +16,7 @@ src_files = Split(""" 4_textured_cube/app.cpp 5_input/app.cpp 6_ui/app.cpp + 6_ui/ui/ui_renderer.cpp """) shader_files = env.Glob("#assets/shaders/glsl/*.frag") \ diff --git a/private/sdl_gpu_test/application.hpp b/private/sdl_gpu_test/application.hpp index 771cb44..13094e4 100644 --- a/private/sdl_gpu_test/application.hpp +++ b/private/sdl_gpu_test/application.hpp @@ -40,6 +40,15 @@ protected: public: virtual ~Application() noexcept = default; + [[nodiscard]] + const sdlpp::Window& getWindow() const noexcept { return mWindow; } + + [[nodiscard]] + const sdlpp::GPUDevice& getDevice() const noexcept { return mDevice; } + + [[nodiscard]] + mijin::StackedFileSystemAdapter& getFileSystem() noexcept { return mFileSystem; } + virtual void init(const AppInitArgs& args); virtual void cleanup(const AppCleanupArgs& args); virtual void update(const AppUpdateArgs& args);