Added simple post-processing.
This commit is contained in:
parent
559ee5f3c3
commit
16135d8fcd
19
assets/shaders/glsl/post_process.comp
Normal file
19
assets/shaders/glsl/post_process.comp
Normal file
@ -0,0 +1,19 @@
|
||||
#version 460
|
||||
|
||||
layout(local_size_x = 16, local_size_y = 16, local_size_x = 1) in;
|
||||
|
||||
layout(set = 0, binding = 0, rgba8) uniform readonly image2D u_sourceImage;
|
||||
layout(set = 1, binding = 0, rgba8) uniform writeonly image2D u_targetImage;
|
||||
|
||||
float luma(vec4 color)
|
||||
{
|
||||
return dot(color.rgb, vec3(0.299, 0.587, 0.114));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
const ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
|
||||
const vec4 pixel = imageLoad(u_sourceImage, uv);
|
||||
const float lum = luma(pixel);
|
||||
imageStore(u_targetImage, uv, vec4(lum, lum, lum, 1.0));
|
||||
}
|
@ -121,11 +121,7 @@ void InputApp::init(const AppInitArgs& args)
|
||||
mSampler.create(mDevice, {});
|
||||
|
||||
// open gamepad
|
||||
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads();
|
||||
if (!gamepads.empty())
|
||||
{
|
||||
mGamepad.open(gamepads[0]);
|
||||
}
|
||||
mGamepad = openFirstGamepad();
|
||||
}
|
||||
|
||||
void InputApp::update(const AppUpdateArgs& args)
|
||||
|
@ -64,11 +64,7 @@ void UIApp::init(const AppInitArgs& args)
|
||||
mSampler.create(mDevice, {});
|
||||
|
||||
// open gamepad
|
||||
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads();
|
||||
if (!gamepads.empty())
|
||||
{
|
||||
mGamepad.open(gamepads[0]);
|
||||
}
|
||||
mGamepad = openFirstGamepad();
|
||||
|
||||
// init the UI
|
||||
mWidgetTree.init({.renderer = &mUIRenderer});
|
||||
|
@ -43,10 +43,7 @@ void ThreeDSceneApp::init(const AppInitArgs& args)
|
||||
|
||||
// open gamepad
|
||||
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads();
|
||||
if (!gamepads.empty())
|
||||
{
|
||||
mGamepad.open(gamepads[0]);
|
||||
}
|
||||
mGamepad = openFirstGamepad();
|
||||
|
||||
// init the UI
|
||||
mWidgetTree.init({.renderer = &mUIRenderer});
|
||||
@ -57,21 +54,10 @@ void ThreeDSceneApp::init(const AppInitArgs& args)
|
||||
.posY = 100,
|
||||
.textHeight = 20
|
||||
});
|
||||
mButton = mWidgetTree.getRootWidget().emplaceChild<Button>({
|
||||
.text = "Click Me!",
|
||||
.posX = 100,
|
||||
.posY = 500
|
||||
});
|
||||
|
||||
// init the scene
|
||||
mScene.init({.renderer = &mSceneRenderer});
|
||||
loadScene("scenes/test_scene.yml", mScene);
|
||||
// mScene.getRootNode().emplaceChild<MeshNode>({.mesh = "meshes/cube.obj", .texture = "meshes/cube.png"});
|
||||
|
||||
mButton->clicked.connect([&]()
|
||||
{
|
||||
mButton->setText("Thanks!");
|
||||
});
|
||||
|
||||
mWindow.setRelativeMouseMode(true);
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_7_3D_SCENE_APP_HPP_INCLUDED 1
|
||||
|
||||
#include "../application.hpp"
|
||||
#include "../gui/button.hpp"
|
||||
#include "../gui/label.hpp"
|
||||
#include "../gui/widget.hpp"
|
||||
#include "../gui/ui_renderer.hpp"
|
||||
@ -27,7 +26,6 @@ private:
|
||||
Scene mScene;
|
||||
|
||||
Label* mLabel = nullptr;
|
||||
Button* mButton = nullptr;
|
||||
|
||||
sdlpp::Gamepad mGamepad;
|
||||
Uint32 mLastSwapchainWidth = 1280;
|
||||
|
263
private/sdl_gpu_test/8_post_process/app.cpp
Normal file
263
private/sdl_gpu_test/8_post_process/app.cpp
Normal file
@ -0,0 +1,263 @@
|
||||
|
||||
#include "./app.hpp"
|
||||
|
||||
#include <numbers>
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "../scene/mesh.hpp"
|
||||
#include "../scene/loader.hpp"
|
||||
#include "../sdlpp/keyboard.hpp"
|
||||
#include "../util/bitmap.hpp"
|
||||
#include "../util/mesh.hpp"
|
||||
#include "../util/spdlog_wrapper.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline constexpr float PITCH_MIN = -0.49f * std::numbers::pi_v<float>;
|
||||
inline constexpr float PITCH_MAX = 0.49f * std::numbers::pi_v<float>;
|
||||
inline constexpr Uint16 AXIS_DEADZONE = 5000;
|
||||
inline constexpr sdlpp::GPUTextureFormat COLOR_BUFFER_FORMAT = sdlpp::GPUTextureFormat::R32G32B32A32_FLOAT;
|
||||
inline constexpr sdlpp::GPUTextureFormat POSTPROCESS_BUFFER_FORMAT = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM;
|
||||
}
|
||||
|
||||
void PostProcessApp::init(const AppInitArgs& args)
|
||||
{
|
||||
Application::init(args);
|
||||
|
||||
// create color buffer
|
||||
mColorBuffer.create(mDevice, {
|
||||
.format = sdlpp::GPUTextureFormat::R32G32B32A32_FLOAT,
|
||||
.usage = sdlpp::GPUTextureUsageFlags{.colorTarget = true, .computeStorageRead = true},
|
||||
.width = 1280,
|
||||
.height = 720
|
||||
});
|
||||
|
||||
// create depth buffer
|
||||
mDepthBuffer.create(mDevice, {
|
||||
.format = sdlpp::GPUTextureFormat::D16_UNORM,
|
||||
.usage = sdlpp::GPUTextureUsageFlags{.depthStencilTarget = true},
|
||||
.width = 1280,
|
||||
.height = 720
|
||||
});
|
||||
|
||||
// and the target for the compute pass (SDL_gpu doesn't support using the swapchain in compute shaders)
|
||||
mPostProcessBuffer.create(mDevice, {
|
||||
.format = POSTPROCESS_BUFFER_FORMAT,
|
||||
.usage = sdlpp::GPUTextureUsageFlags{.computeStorageWrite = true},
|
||||
.width = 1280,
|
||||
.height = 720
|
||||
});
|
||||
|
||||
// init UI renderer
|
||||
mUIRenderer.init(*this);
|
||||
|
||||
// init scene renderer
|
||||
mSceneRenderer.init(*this, {
|
||||
.colorBufferFormat = COLOR_BUFFER_FORMAT
|
||||
});
|
||||
|
||||
// and the post processor
|
||||
mPostProcessor.init(*this);
|
||||
|
||||
// open gamepad
|
||||
mGamepad = openFirstGamepad();
|
||||
|
||||
// init the UI
|
||||
mWidgetTree.init({.renderer = &mUIRenderer});
|
||||
mLabel = mWidgetTree.getRootWidget().emplaceChild<Label>({
|
||||
.text = "Test-Text!\nSecond $f00line$fff?",
|
||||
.color = glm::vec4(0.f, 1.f, 0.f, 1.f),
|
||||
.posX = 100,
|
||||
.posY = 100,
|
||||
.textHeight = 20
|
||||
});
|
||||
|
||||
// init the scene
|
||||
mScene.init({.renderer = &mSceneRenderer});
|
||||
loadScene("scenes/test_scene.yml", mScene);
|
||||
|
||||
mWindow.setRelativeMouseMode(true);
|
||||
}
|
||||
|
||||
void PostProcessApp::update(const AppUpdateArgs& args)
|
||||
{
|
||||
Application::update(args);
|
||||
|
||||
processInput(args);
|
||||
mLabel->setText(std::format("Pitch: {:.1f}\nYaw: {:.1f}", mPitch, mYaw));
|
||||
mWidgetTree.revalidateWidgets();
|
||||
|
||||
// begin rendering
|
||||
sdlpp::GPUCommandBuffer cmdBuffer = mDevice.acquireCommandBuffer();
|
||||
Uint32 swapchainWidth = 0, swapchainHeight = 0;
|
||||
const sdlpp::GPUTexture swapchainTexture = cmdBuffer.acquireSwapchainTexture(mWindow, swapchainWidth, swapchainHeight);
|
||||
|
||||
if (swapchainWidth != mLastSwapchainWidth || swapchainHeight != mLastSwapchainHeight)
|
||||
{
|
||||
mColorBuffer.destroy();
|
||||
mColorBuffer.create(mDevice, {
|
||||
.format = COLOR_BUFFER_FORMAT,
|
||||
.usage = sdlpp::GPUTextureUsageFlags{.colorTarget = true, .computeStorageRead = true},
|
||||
.width = swapchainWidth,
|
||||
.height = swapchainHeight
|
||||
});
|
||||
mDepthBuffer.destroy();
|
||||
mDepthBuffer.create(mDevice, {
|
||||
.format = sdlpp::GPUTextureFormat::D16_UNORM,
|
||||
.usage = sdlpp::GPUTextureUsageFlags{.depthStencilTarget = true},
|
||||
.width = swapchainWidth,
|
||||
.height = swapchainHeight
|
||||
});
|
||||
mPostProcessBuffer.destroy();
|
||||
mPostProcessBuffer.create(mDevice, {
|
||||
.format = POSTPROCESS_BUFFER_FORMAT,
|
||||
.usage = sdlpp::GPUTextureUsageFlags{.computeStorageWrite = true},
|
||||
.width = swapchainWidth,
|
||||
.height = swapchainHeight
|
||||
});
|
||||
mLastSwapchainWidth = swapchainWidth;
|
||||
mLastSwapchainHeight = swapchainHeight;
|
||||
}
|
||||
|
||||
// render the scene
|
||||
mSceneRenderer.render({
|
||||
.camera = {
|
||||
.position = mPosition,
|
||||
.pitch = mPitch,
|
||||
.yaw = mYaw
|
||||
},
|
||||
.cmdBuffer = &cmdBuffer,
|
||||
.targetTexture = &mColorBuffer,
|
||||
.depthTexture = &mDepthBuffer,
|
||||
.targetTextureWidth = swapchainWidth,
|
||||
.targetTextureHeight = swapchainHeight
|
||||
});
|
||||
|
||||
// do the post processing
|
||||
mPostProcessor.execute({
|
||||
.cmdBuffer = &cmdBuffer,
|
||||
.sourceTexture = &mColorBuffer,
|
||||
.targetTexture = &mPostProcessBuffer,
|
||||
.textureWidth = swapchainWidth,
|
||||
.textureHeight = swapchainHeight
|
||||
});
|
||||
|
||||
// copy the result to the swapchain image
|
||||
sdlpp::GPUCopyPass copyPass = cmdBuffer.beginCopyPass();
|
||||
copyPass.copyTextureToTexture(
|
||||
/* source = */ {.texture = mPostProcessBuffer},
|
||||
/* destination = */ {.texture = swapchainTexture},
|
||||
/* width = */ swapchainWidth,
|
||||
/* height = */ swapchainHeight
|
||||
);
|
||||
copyPass.end();
|
||||
|
||||
// render the "UI"
|
||||
mUIRenderer.render({
|
||||
.cmdBuffer = &cmdBuffer,
|
||||
.targetTexture = &swapchainTexture,
|
||||
.targetTextureWidth = swapchainWidth,
|
||||
.targetTextureHeight = swapchainHeight
|
||||
});
|
||||
|
||||
// finalize
|
||||
cmdBuffer.submit();
|
||||
}
|
||||
|
||||
void PostProcessApp::handleKeyboardEvent(const sdlpp::KeyboardEvent& event)
|
||||
{
|
||||
switch (event.key)
|
||||
{
|
||||
case SDLK_Q:
|
||||
if (!event.down)
|
||||
{
|
||||
mRunning = false;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void PostProcessApp::handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event)
|
||||
{
|
||||
mWidgetTree.notifyMouseMoved(event);
|
||||
|
||||
mYaw -= 0.002f * event.xrel;
|
||||
mPitch -= 0.002f * event.yrel;
|
||||
}
|
||||
|
||||
void PostProcessApp::handleMouseButtonEvent(const sdlpp::MouseButtonEvent& event)
|
||||
{
|
||||
mWidgetTree.notifyMouseButton(event);
|
||||
}
|
||||
|
||||
void PostProcessApp::processInput(const AppUpdateArgs& args)
|
||||
{
|
||||
glm::vec2 movement = {0.f, 0.f};
|
||||
const std::span<const SDL_bool> keyboardState = sdlpp::getKeyboardState();
|
||||
if (mGamepad)
|
||||
{
|
||||
Sint16 axisValue = mGamepad.getAxis(sdlpp::GamepadAxis::RIGHTX);
|
||||
if (std::abs(axisValue) > AXIS_DEADZONE)
|
||||
{
|
||||
mYaw -= 0.0001f * args.tickSeconds * static_cast<float>(axisValue);
|
||||
}
|
||||
|
||||
axisValue = mGamepad.getAxis(sdlpp::GamepadAxis::RIGHTY);
|
||||
if (std::abs(axisValue) > AXIS_DEADZONE)
|
||||
{
|
||||
mPitch -= 0.0001f * args.tickSeconds * static_cast<float>(axisValue);
|
||||
}
|
||||
|
||||
axisValue = mGamepad.getAxis(sdlpp::GamepadAxis::LEFTY);
|
||||
if (std::abs(axisValue) > AXIS_DEADZONE)
|
||||
{
|
||||
movement.y -= static_cast<float>(axisValue) / static_cast<float>(std::numeric_limits<Sint16>::max());
|
||||
}
|
||||
|
||||
axisValue = mGamepad.getAxis(sdlpp::GamepadAxis::LEFTX);
|
||||
if (std::abs(axisValue) > AXIS_DEADZONE)
|
||||
{
|
||||
movement.x += static_cast<float>(axisValue) / static_cast<float>(std::numeric_limits<Sint16>::max());
|
||||
}
|
||||
}
|
||||
|
||||
if (keyboardState[SDL_SCANCODE_W])
|
||||
{
|
||||
movement.y += 1.f;
|
||||
}
|
||||
if (keyboardState[SDL_SCANCODE_S])
|
||||
{
|
||||
movement.y -= 1.f;
|
||||
}
|
||||
if (keyboardState[SDL_SCANCODE_A])
|
||||
{
|
||||
movement.x -= 1.f;
|
||||
}
|
||||
if (keyboardState[SDL_SCANCODE_D])
|
||||
{
|
||||
movement.x += 1.f;
|
||||
}
|
||||
|
||||
if (glm::length2(movement) > 1.f)
|
||||
{
|
||||
movement = glm::normalize(movement);
|
||||
}
|
||||
|
||||
mPosition.x -= 2.f * args.tickSeconds * std::sin(mYaw) * movement.y;
|
||||
mPosition.z -= 2.f * args.tickSeconds * std::cos(mYaw) * movement.y;
|
||||
|
||||
mPosition.x += 2.f * args.tickSeconds * std::cos(mYaw) * movement.x;
|
||||
mPosition.z -= 2.f * args.tickSeconds * std::sin(mYaw) * movement.x;
|
||||
|
||||
while (mYaw >= 2.f * std::numbers::pi) { mYaw -= 2.f * std::numbers::pi_v<float>; }
|
||||
while (mYaw < 0.f) { mYaw += 2.f * std::numbers::pi_v<float>; }
|
||||
mPitch = std::clamp(mPitch, PITCH_MIN, PITCH_MAX);
|
||||
}
|
||||
}
|
52
private/sdl_gpu_test/8_post_process/app.hpp
Normal file
52
private/sdl_gpu_test/8_post_process/app.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_8_POST_PROCESS_APP_HPP_INCLUDED)
|
||||
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_8_POST_PROCESS_APP_HPP_INCLUDED 1
|
||||
|
||||
#include "../application.hpp"
|
||||
#include "../gui/label.hpp"
|
||||
#include "../gui/widget.hpp"
|
||||
#include "../gui/ui_renderer.hpp"
|
||||
#include "../post_process/post_processor.hpp"
|
||||
#include "../scene/scene.hpp"
|
||||
#include "../scene/scene_renderer.hpp"
|
||||
#include "../sdlpp/gamepad.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
class PostProcessApp : public Application
|
||||
{
|
||||
private:
|
||||
sdlpp::GPUTexture mColorBuffer;
|
||||
sdlpp::GPUTexture mDepthBuffer;
|
||||
sdlpp::GPUTexture mPostProcessBuffer;
|
||||
|
||||
UIRenderer mUIRenderer;
|
||||
WidgetTree mWidgetTree;
|
||||
|
||||
SceneRenderer mSceneRenderer;
|
||||
Scene mScene;
|
||||
|
||||
PostProcessor mPostProcessor;
|
||||
|
||||
Label* mLabel = nullptr;
|
||||
|
||||
sdlpp::Gamepad mGamepad;
|
||||
Uint32 mLastSwapchainWidth = 1280;
|
||||
Uint32 mLastSwapchainHeight = 720;
|
||||
glm::vec3 mPosition = {5.f, 0.f, -7.f};
|
||||
float mYaw = 1.57f;
|
||||
float mPitch = 0.f;
|
||||
public:
|
||||
void init(const AppInitArgs& args) override;
|
||||
void update(const AppUpdateArgs& args) override;
|
||||
void handleKeyboardEvent(const sdlpp::KeyboardEvent& event) override;
|
||||
void handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event) override;
|
||||
void handleMouseButtonEvent(const sdlpp::MouseButtonEvent&) override;
|
||||
private:
|
||||
void processInput(const AppUpdateArgs& args);
|
||||
};
|
||||
} // namespace sdl_gpu_test
|
||||
|
||||
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_8_POST_PROCESS_APP_HPP_INCLUDED)
|
@ -9,6 +9,7 @@ src_files = Split("""
|
||||
gui/label.cpp
|
||||
gui/ui_renderer.cpp
|
||||
gui/widget.cpp
|
||||
post_process/post_processor.cpp
|
||||
scene/loader.cpp
|
||||
scene/mesh.cpp
|
||||
scene/scene.cpp
|
||||
@ -26,10 +27,12 @@ src_files = Split("""
|
||||
5_input/app.cpp
|
||||
6_ui/app.cpp
|
||||
7_3d_scene/app.cpp
|
||||
8_post_process/app.cpp
|
||||
""")
|
||||
|
||||
shader_files = env.Glob("#assets/shaders/glsl/*.frag") \
|
||||
+ env.Glob("#assets/shaders/glsl/*.vert")
|
||||
+ env.Glob("#assets/shaders/glsl/*.vert") \
|
||||
+ env.Glob("#assets/shaders/glsl/*.comp")
|
||||
|
||||
env.Append(CPPDEFINES = ['GLM_FORCE_DEPTH_ZERO_TO_ONE', 'GLM_FORCE_RADIANS', 'GLM_ENABLE_EXPERIMENTAL'])
|
||||
prog_app = env.UnityProgram(
|
||||
|
@ -114,6 +114,16 @@ sdlpp::GPUShader Application::loadShader(const fs::path& path, sdlpp::GPUShaderC
|
||||
return shader;
|
||||
}
|
||||
|
||||
sdlpp::GPUComputePipeline Application::loadComputePipeline(const fs::path& path, sdlpp::GPUComputePipelineCreateArgs args)
|
||||
{
|
||||
mijin::TypelessBuffer source = getFileContentsBinary(path);
|
||||
sdlpp::GPUComputePipeline pipeline;
|
||||
args.code = {static_cast<const Uint8*>(source.data()), source.byteSize()};
|
||||
pipeline.create(mDevice, args);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
|
||||
sdlpp::GPUTexture Application::loadTexture(const fs::path& path, sdlpp::GPUTextureCreateArgs& inout_args)
|
||||
{
|
||||
const Bitmap bitmap = loadBitmap(mFileSystem.getPath(path));
|
||||
@ -159,4 +169,26 @@ void Application::uploadTextureData(const sdlpp::GPUTexture& texture, const Bitm
|
||||
copyPass.end();
|
||||
cmdBuffer.submit();
|
||||
}
|
||||
|
||||
sdlpp::Gamepad Application::openFirstGamepad() const
|
||||
{
|
||||
sdlpp::Gamepad gamepad;
|
||||
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads();
|
||||
if (!gamepads.empty())
|
||||
{
|
||||
for (SDL_JoystickID gamepadId : gamepads)
|
||||
{
|
||||
try
|
||||
{
|
||||
gamepad.open(gamepadId);
|
||||
break;
|
||||
}
|
||||
catch(std::runtime_error& err)
|
||||
{
|
||||
spdlog::warn("Error opening gamepad: {}.", err.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
return gamepad;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <mijin/virtual_filesystem/stacked.hpp>
|
||||
|
||||
#include "./sdlpp/event.hpp"
|
||||
#include "./sdlpp/gamepad.hpp"
|
||||
#include "./sdlpp/gpu.hpp"
|
||||
#include "./sdlpp/window.hpp"
|
||||
|
||||
@ -74,6 +75,9 @@ public:
|
||||
[[nodiscard]]
|
||||
sdlpp::GPUShader loadShader(const fs::path& path, sdlpp::GPUShaderCreateArgs args);
|
||||
|
||||
[[nodiscard]]
|
||||
sdlpp::GPUComputePipeline loadComputePipeline(const fs::path& path, sdlpp::GPUComputePipelineCreateArgs args);
|
||||
|
||||
[[nodiscard]]
|
||||
sdlpp::GPUTexture loadTexture(const fs::path& path, sdlpp::GPUTextureCreateArgs& inout_args);
|
||||
|
||||
@ -81,6 +85,9 @@ public:
|
||||
|
||||
void uploadTextureData(const sdlpp::GPUTexture& texture, const struct Bitmap& bitmap) const;
|
||||
|
||||
[[nodiscard]]
|
||||
sdlpp::Gamepad openFirstGamepad() const;
|
||||
|
||||
template<typename TVertex>
|
||||
void uploadVertexData(const sdlpp::GPUBuffer& vertexBuffer, std::span<TVertex> vertices) const;
|
||||
};
|
||||
|
@ -10,7 +10,9 @@
|
||||
#include "./5_input/app.hpp"
|
||||
#include "./6_ui/app.hpp"
|
||||
#include "./7_3d_scene/app.hpp"
|
||||
#include "./8_post_process/app.hpp"
|
||||
#include "./util/spdlog_wrapper.hpp"
|
||||
#include "8_post_process/app.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -40,7 +42,8 @@ const std::array APPS = {
|
||||
makeAppHelper<sdl_gpu_test::TexturedCubeApp>("textured_cube"),
|
||||
makeAppHelper<sdl_gpu_test::InputApp>("input"),
|
||||
makeAppHelper<sdl_gpu_test::UIApp>("ui"),
|
||||
makeAppHelper<sdl_gpu_test::ThreeDSceneApp>("3d_scene")
|
||||
makeAppHelper<sdl_gpu_test::ThreeDSceneApp>("3d_scene"),
|
||||
makeAppHelper<sdl_gpu_test::PostProcessApp>("post_process")
|
||||
};
|
||||
}
|
||||
|
||||
|
47
private/sdl_gpu_test/post_process/post_processor.cpp
Normal file
47
private/sdl_gpu_test/post_process/post_processor.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
#include "./post_processor.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline constexpr Uint32 LOCAL_SIZE_X = 16;
|
||||
inline constexpr Uint32 LOCAL_SIZE_Y = 16;
|
||||
}
|
||||
|
||||
void PostProcessor::init(Application& application)
|
||||
{
|
||||
mApplication = &application;
|
||||
|
||||
createPipeline();
|
||||
}
|
||||
|
||||
void PostProcessor::createPipeline()
|
||||
{
|
||||
mPipeline = mApplication->loadComputePipeline("shaders/glsl/post_process.comp.spv", {
|
||||
.format = sdlpp::GPUShaderFormat::SPIRV,
|
||||
.numReadonlyStorageTextures = 1,
|
||||
.numWriteonlyStorageTextures = 1,
|
||||
.threadcountX = LOCAL_SIZE_X,
|
||||
.threadcountY = LOCAL_SIZE_Y
|
||||
});
|
||||
}
|
||||
|
||||
void PostProcessor::execute(const PostProcessorExecuteArgs& args)
|
||||
{
|
||||
const std::array textueBindings = {
|
||||
sdlpp::GPUStorageTextureWriteOnlyBinding{
|
||||
.texture = *args.targetTexture,
|
||||
.cycle = true
|
||||
}
|
||||
};
|
||||
sdlpp::GPUComputePass computePass = args.cmdBuffer->beginComputePass({.storageTextureBindings = textueBindings});
|
||||
computePass.bindComputePipeline(mPipeline);
|
||||
computePass.bindStorageTexture(0, *args.sourceTexture);
|
||||
computePass.dispatch(
|
||||
/* groupCountX = */ (args.textureWidth + LOCAL_SIZE_X - 1) / LOCAL_SIZE_X,
|
||||
/* groupCountY = */ (args.textureHeight + LOCAL_SIZE_Y - 1) / LOCAL_SIZE_Y
|
||||
);
|
||||
computePass.end();
|
||||
}
|
||||
}
|
35
private/sdl_gpu_test/post_process/post_processor.hpp
Normal file
35
private/sdl_gpu_test/post_process/post_processor.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_POST_PROCESS_POST_PROCESSOR_HPP_INCLUDED)
|
||||
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_POST_PROCESS_POST_PROCESSOR_HPP_INCLUDED 1
|
||||
|
||||
#include "../application.hpp"
|
||||
#include "../sdlpp/gpu.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
struct PostProcessorExecuteArgs
|
||||
{
|
||||
const sdlpp::GPUCommandBuffer* cmdBuffer;
|
||||
const sdlpp::GPUTexture* sourceTexture;
|
||||
const sdlpp::GPUTexture* targetTexture;
|
||||
unsigned textureWidth;
|
||||
unsigned textureHeight;
|
||||
};
|
||||
|
||||
class PostProcessor
|
||||
{
|
||||
private:
|
||||
Application* mApplication = nullptr;
|
||||
sdlpp::GPUComputePipeline mPipeline;
|
||||
public:
|
||||
void init(Application& application);
|
||||
|
||||
void execute(const PostProcessorExecuteArgs& args);
|
||||
private:
|
||||
void createPipeline();
|
||||
};
|
||||
} // namespace sdl_gpu_test
|
||||
|
||||
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_POST_PROCESS_POST_PROCESSOR_HPP_INCLUDED)
|
@ -24,11 +24,17 @@ struct PerModelParameters
|
||||
glm::mat4 modelToWorld;
|
||||
};
|
||||
}
|
||||
void SceneRenderer::init(Application& application)
|
||||
void SceneRenderer::init(Application& application, SceneRendererInitArgs args)
|
||||
{
|
||||
mApplication = &application;
|
||||
|
||||
createPipeline();
|
||||
if (args.colorBufferFormat == sdlpp::GPUTextureFormat::INVALID)
|
||||
{
|
||||
// default to render directly to the swapchain
|
||||
args.colorBufferFormat = mApplication->getDevice().getSwapchainTextureFormat(mApplication->getWindow());
|
||||
}
|
||||
|
||||
createPipeline(args);
|
||||
mSampler.create(mApplication->getDevice(), {});
|
||||
}
|
||||
|
||||
@ -100,6 +106,7 @@ void SceneRenderer::render(const SceneRendererRenderArgs& args)
|
||||
.texture = *args.targetTexture,
|
||||
.clearColor = {.r = 0.f, .g = 0.f, .b = 0.f, .a = 1.f},
|
||||
.loadOp = sdlpp::GPULoadOp::CLEAR,
|
||||
.cycle = true
|
||||
}};
|
||||
sdlpp::GPURenderPass renderPass = args.cmdBuffer->beginRenderPass({
|
||||
.colorTargetInfos = colorTargets,
|
||||
@ -193,7 +200,7 @@ bool SceneRenderer::updateRenderListEntry(mesh_id_t meshId, const RenderListUpda
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneRenderer::createPipeline()
|
||||
void SceneRenderer::createPipeline(const SceneRendererInitArgs& args)
|
||||
{
|
||||
// create shaders
|
||||
const sdlpp::GPUShader vertexShader = mApplication->loadShader("shaders/glsl/scene_renderer.vert.spv", {
|
||||
@ -209,7 +216,7 @@ void SceneRenderer::createPipeline()
|
||||
});
|
||||
std::array colorTargetsDescs = {
|
||||
sdlpp::GPUColorTargetDescription{
|
||||
.format = mApplication->getDevice().getSwapchainTextureFormat(mApplication->getWindow()),
|
||||
.format = args.colorBufferFormat,
|
||||
.blendState = {
|
||||
.enableBlend = true,
|
||||
.srcColorBlendfactor = sdlpp::GPUBlendFactor::SRC_ALPHA,
|
||||
|
@ -37,6 +37,11 @@ struct CameraOptions
|
||||
float zFar = 100.f;
|
||||
};
|
||||
|
||||
struct SceneRendererInitArgs
|
||||
{
|
||||
sdlpp::GPUTextureFormat colorBufferFormat = sdlpp::GPUTextureFormat::INVALID;
|
||||
};
|
||||
|
||||
struct SceneRendererRenderArgs
|
||||
{
|
||||
CameraOptions camera;
|
||||
@ -78,7 +83,7 @@ private:
|
||||
sdlpp::GPUGraphicsPipeline mPipeline;
|
||||
sdlpp::GPUSampler mSampler;
|
||||
public:
|
||||
void init(Application& application);
|
||||
void init(Application& application, SceneRendererInitArgs args = {});
|
||||
|
||||
[[nodiscard]]
|
||||
std::shared_ptr<SceneMesh> getMesh(const std::string& resourcePath);
|
||||
@ -93,7 +98,7 @@ public:
|
||||
|
||||
void render(const SceneRendererRenderArgs& args);
|
||||
private:
|
||||
void createPipeline();
|
||||
void createPipeline(const SceneRendererInitArgs& args);
|
||||
};
|
||||
} // namespace sdl_gpu_test
|
||||
|
||||
|
@ -475,6 +475,26 @@ struct GPUDepthStencilTargetInfo
|
||||
static_assert(sizeof(GPUDepthStencilTargetInfo) == sizeof(SDL_GPUDepthStencilTargetInfo)
|
||||
&& alignof(GPUDepthStencilTargetInfo) == alignof(SDL_GPUDepthStencilTargetInfo));
|
||||
|
||||
struct GPUStorageTextureWriteOnlyBinding
|
||||
{
|
||||
SDL_GPUTexture* texture = nullptr;
|
||||
Uint32 mipLevel = 0;
|
||||
Uint32 layer = 0;
|
||||
bool cycle = false;
|
||||
std::array<Uint8, 3> padding_;
|
||||
};
|
||||
static_assert(sizeof(GPUStorageTextureWriteOnlyBinding) == sizeof(SDL_GPUStorageTextureWriteOnlyBinding)
|
||||
&& alignof(GPUStorageTextureWriteOnlyBinding) == alignof(SDL_GPUStorageTextureWriteOnlyBinding));
|
||||
|
||||
struct GPUStorageBufferWriteOnlyBinding
|
||||
{
|
||||
SDL_GPUBuffer* buffer = nullptr;
|
||||
bool cycle = false;
|
||||
std::array<Uint8, 3> padding;
|
||||
};
|
||||
static_assert(sizeof(GPUStorageBufferWriteOnlyBinding) == sizeof(SDL_GPUStorageBufferWriteOnlyBinding)
|
||||
&& alignof(GPUStorageBufferWriteOnlyBinding) == alignof(SDL_GPUStorageBufferWriteOnlyBinding));
|
||||
|
||||
struct GPUTransferBufferLocation
|
||||
{
|
||||
SDL_GPUTransferBuffer* transferBuffer;
|
||||
@ -513,6 +533,17 @@ struct GPUTextureRegion
|
||||
};
|
||||
static_assert(sizeof(GPUTextureRegion) == sizeof(SDL_GPUTextureRegion));
|
||||
|
||||
struct GPUTextureLocation
|
||||
{
|
||||
SDL_GPUTexture* texture = nullptr;
|
||||
Uint32 mipLevel = 0;
|
||||
Uint32 layer = 0;
|
||||
Uint32 x = 0;
|
||||
Uint32 y = 0;
|
||||
Uint32 z = 0;
|
||||
};
|
||||
static_assert(sizeof(GPUTextureLocation) == sizeof(SDL_GPUTextureLocation));
|
||||
|
||||
using GPUBufferBinding = SDL_GPUBufferBinding;
|
||||
|
||||
using GPUTextureSamplerBinding = SDL_GPUTextureSamplerBinding;
|
||||
@ -598,6 +629,60 @@ public:
|
||||
friend class GPUCommandBuffer;
|
||||
};
|
||||
|
||||
class GPUComputePass : public Base<SDL_GPUComputePass, GPUComputePass>
|
||||
{
|
||||
public:
|
||||
GPUComputePass() noexcept = default;
|
||||
GPUComputePass(const GPUComputePass&) = delete;
|
||||
GPUComputePass(GPUComputePass&& other) noexcept : Base(std::move(other)) {}
|
||||
|
||||
GPUComputePass& operator=(const GPUComputePass&) = delete;
|
||||
GPUComputePass& operator=(GPUComputePass&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUComputePass& other) const noexcept = default;
|
||||
|
||||
void end() noexcept
|
||||
{
|
||||
SDL_EndGPUComputePass(mHandle);
|
||||
mHandle = nullptr;
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
MIJIN_ASSERT(mHandle == nullptr, "Computepass has not been ended.");
|
||||
}
|
||||
|
||||
void bindComputePipeline(SDL_GPUComputePipeline* computePipeline) const noexcept
|
||||
{
|
||||
SDL_BindGPUComputePipeline(mHandle, computePipeline);
|
||||
}
|
||||
|
||||
void bindStorageTextures(Uint32 slotIndex, std::span<SDL_GPUTexture* const> storageTextures) const noexcept
|
||||
{
|
||||
SDL_BindGPUComputeStorageTextures(
|
||||
/* compute_pass = */ mHandle,
|
||||
/* first_slot = */ slotIndex,
|
||||
/* storage_textures = */ storageTextures.data(),
|
||||
/* num_bindings = */ static_cast<Uint32>(storageTextures.size())
|
||||
);
|
||||
}
|
||||
|
||||
void bindStorageTexture(Uint32 slotIndex, SDL_GPUTexture* storageTexture) const noexcept
|
||||
{
|
||||
bindStorageTextures(slotIndex, {&storageTexture, 1});
|
||||
}
|
||||
|
||||
void dispatch(Uint32 groupCountX, Uint32 groupCountY = 1, Uint32 groupCountZ = 1) const noexcept
|
||||
{
|
||||
SDL_DispatchGPUCompute(mHandle, groupCountX, groupCountY, groupCountZ);
|
||||
}
|
||||
|
||||
friend class GPUCommandBuffer;
|
||||
};
|
||||
|
||||
class GPUCopyPass : public Base<SDL_GPUCopyPass, GPUCopyPass>
|
||||
{
|
||||
public:
|
||||
@ -644,6 +729,20 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
void copyTextureToTexture(const GPUTextureLocation& source, const GPUTextureLocation& destination, Uint32 width,
|
||||
Uint32 height, Uint32 depth = 1, bool cycle = false) const noexcept
|
||||
{
|
||||
SDL_CopyGPUTextureToTexture(
|
||||
/* copy_pass = */ mHandle,
|
||||
/* source = */ std::bit_cast<const SDL_GPUTextureLocation*>(&source),
|
||||
/* destination = */ std::bit_cast<const SDL_GPUTextureLocation*>(&destination),
|
||||
/* w = */ width,
|
||||
/* h = */ height,
|
||||
/* d = */ depth,
|
||||
/* cycle = */ cycle
|
||||
);
|
||||
}
|
||||
|
||||
friend class GPUCommandBuffer;
|
||||
};
|
||||
|
||||
@ -787,6 +886,12 @@ struct GPUBeginRenderPassArgs
|
||||
std::optional<GPUDepthStencilTargetInfo> depthStencilTargetInfo;
|
||||
};
|
||||
|
||||
struct GPUBeginComputePassArgs
|
||||
{
|
||||
std::span<const GPUStorageTextureWriteOnlyBinding> storageTextureBindings;
|
||||
std::span<const GPUStorageBufferWriteOnlyBinding> storageBufferBindings;
|
||||
};
|
||||
|
||||
class GPUCommandBuffer : public Base<SDL_GPUCommandBuffer, GPUCommandBuffer>
|
||||
{
|
||||
public:
|
||||
@ -826,6 +931,20 @@ public:
|
||||
return renderPass;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
GPUComputePass beginComputePass(const GPUBeginComputePassArgs& args) const noexcept
|
||||
{
|
||||
GPUComputePass computePass;
|
||||
computePass.mHandle = SDL_BeginGPUComputePass(
|
||||
/* command_buffer = */ mHandle,
|
||||
/* storage_texture_bindings = */ std::bit_cast<const SDL_GPUStorageTextureWriteOnlyBinding*>(args.storageTextureBindings.data()),
|
||||
/* num_storage_texture_bindings = */ static_cast<Uint32>(args.storageTextureBindings.size()),
|
||||
/* storage_buffer_bindings = */ std::bit_cast<const SDL_GPUStorageBufferWriteOnlyBinding*>(args.storageBufferBindings.data()),
|
||||
/* num_storage_buffer_bindings = */ static_cast<Uint32>(args.storageBufferBindings.size())
|
||||
);
|
||||
return computePass;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
GPUCopyPass beginCopyPass() const noexcept
|
||||
{
|
||||
@ -1039,6 +1158,73 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUComputePipelineCreateArgs
|
||||
{
|
||||
std::span<const Uint8> code;
|
||||
std::string entrypoint = "main";
|
||||
GPUShaderFormat format;
|
||||
Uint32 numReadonlyStorageTextures = 0;
|
||||
Uint32 numReadonlyStorageBuffers = 0;
|
||||
Uint32 numWriteonlyStorageTextures = 0;
|
||||
Uint32 numWriteonlyStorageBuffers = 0;
|
||||
Uint32 numUniformBuffers = 0;
|
||||
Uint32 threadcountX = 1;
|
||||
Uint32 threadcountY = 1;
|
||||
Uint32 threadcountZ = 1;
|
||||
};
|
||||
|
||||
class GPUComputePipeline : public BaseWithDevice<SDL_GPUComputePipeline, GPUComputePipeline>
|
||||
{
|
||||
public:
|
||||
GPUComputePipeline() noexcept = default;
|
||||
GPUComputePipeline(const GPUComputePipeline&) = delete;
|
||||
GPUComputePipeline(GPUComputePipeline&& other) noexcept : BaseWithDevice(std::move(other)) {}
|
||||
|
||||
GPUComputePipeline& operator=(const GPUComputePipeline&) = delete;
|
||||
GPUComputePipeline& operator=(GPUComputePipeline&& other) noexcept
|
||||
{
|
||||
BaseWithDevice::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUComputePipeline& other) const noexcept = default;
|
||||
|
||||
void create(SDL_GPUDevice* device, const GPUComputePipelineCreateArgs& args)
|
||||
{
|
||||
MIJIN_ASSERT(mHandle == nullptr, "GPUComputePipeline has already been created.");
|
||||
const SDL_GPUComputePipelineCreateInfo createInfo =
|
||||
{
|
||||
.code_size = args.code.size(),
|
||||
.code = args.code.data(),
|
||||
.entrypoint = args.entrypoint.c_str(),
|
||||
.format = static_cast<SDL_GPUShaderFormat>(args.format),
|
||||
.num_readonly_storage_textures = args.numReadonlyStorageTextures,
|
||||
.num_readonly_storage_buffers = args.numReadonlyStorageBuffers,
|
||||
.num_writeonly_storage_textures = args.numWriteonlyStorageTextures,
|
||||
.num_writeonly_storage_buffers = args.numWriteonlyStorageBuffers,
|
||||
.threadcount_x = args.threadcountX,
|
||||
.threadcount_y = args.threadcountY,
|
||||
.threadcount_z = args.threadcountZ,
|
||||
.props = 0
|
||||
};
|
||||
mHandle = SDL_CreateGPUComputePipeline(device, &createInfo);
|
||||
if (mHandle == nullptr)
|
||||
{
|
||||
throw SDLError();
|
||||
}
|
||||
mDevice = device;
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
if (mHandle != nullptr)
|
||||
{
|
||||
SDL_ReleaseGPUComputePipeline(mDevice, mHandle);
|
||||
mDevice = nullptr;
|
||||
mHandle = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUShaderCreateArgs
|
||||
{
|
||||
std::span<const Uint8> code;
|
||||
|
Loading…
x
Reference in New Issue
Block a user