diff --git a/Engine/Source/Render/Src/RenderGraph.cpp b/Engine/Source/Render/Src/RenderGraph.cpp index 2c74e49f..15db55fa 100644 --- a/Engine/Source/Render/Src/RenderGraph.cpp +++ b/Engine/Source/Render/Src/RenderGraph.cpp @@ -22,6 +22,8 @@ namespace Render::Internal { outReads.emplace(std::get(view)->GetResource()); } else if (type == RHI::BindingType::storageTexture) { outWrites.emplace(std::get(view)->GetResource()); + } else if (type == RHI::BindingType::sampler){ + return; } else { Unimplement(); } diff --git a/Sample/CMakeLists.txt b/Sample/CMakeLists.txt index 146d2da1..69db523c 100644 --- a/Sample/CMakeLists.txt +++ b/Sample/CMakeLists.txt @@ -98,3 +98,13 @@ add_sample( SHADER Rendering-Triangle/Triangle.esl ) + +file(GLOB SOURCES Rendering-BaseTexture/*.cpp) +add_sample( + NAME Rendering-BaseTexture + SRC ${SOURCES} + INC Rendering-BaseTexture + SHADER Rendering-BaseTexture/BaseTexture.esl + IMAGE Rendering-BaseTexture/Awesomeface.png +) + diff --git a/Sample/Rendering-BaseTexture/Awesomeface.png b/Sample/Rendering-BaseTexture/Awesomeface.png new file mode 100644 index 00000000..c7bfec66 Binary files /dev/null and b/Sample/Rendering-BaseTexture/Awesomeface.png differ diff --git a/Sample/Rendering-BaseTexture/BaseTexture.cpp b/Sample/Rendering-BaseTexture/BaseTexture.cpp new file mode 100644 index 00000000..6824aeb6 --- /dev/null +++ b/Sample/Rendering-BaseTexture/BaseTexture.cpp @@ -0,0 +1,405 @@ +// +// Created by swtpotato on 2022/10/21. +// + +#include +#define STB_IMAGE_IMPLEMENTATION +#include +#include +#include +#include +#include + +using namespace Common; +using namespace Render; +using namespace RHI; + +struct Vertex { + FVec4 position; + FVec2 uv; +}; + +struct VertUniform { + FMat4x4 modelMatrix; +}; + +class BaseTexVS final : public StaticShaderType { + ShaderTypeInfo( + BaseTexVS, + RHI::ShaderStageBits::sVertex, + "Engine/Test/Sample/Rendering-BaseTexture/BaseTexture.esl", + "VSMain") + + EmptyIncludeDirectories + EmptyVariantFieldVec +}; + +class BaseTexPS final : public StaticShaderType { +public: + ShaderTypeInfo( + BaseTexPS, + RHI::ShaderStageBits::sPixel, + "Engine/Test/Sample/Rendering-BaseTexture/BaseTexture.esl", + "PSMain") + + EmptyIncludeDirectories + EmptyVariantFieldVec +}; + +ImplementStaticShaderType(BaseTexVS); +ImplementStaticShaderType(BaseTexPS); + +class BaseTexApp final : public Application { +public: + NonCopyable(BaseTexApp) + explicit BaseTexApp(const std::string& inName); + ~BaseTexApp() override; + + void OnCreate() override; + void OnDrawFrame() override; + void OnDestroy() override; + +private: + static constexpr size_t backBufferCount = 2; + + void CreateDevice(); + void CreateSurface(); + void CompileAllShaders() const; + void FetchShaderInstances(); + void CreateSwapChain(); + void CreateVertexAndIndexBuffer(); + void CreateTextureAndSampler(); + void CreateSyncObjects(); + + PixelFormat swapChainFormat; + ShaderInstance vs; + ShaderInstance ps; + UniquePtr device; + UniquePtr surface; + UniquePtr swapChain; + UniquePtr sampler; + UniquePtr vertexBuffer; + UniquePtr indexBuffer; + UniquePtr uniformBuffer; + UniquePtr texture; + UniquePtr imageBuffer; + std::array swapChainTextures; + UniquePtr pipeline; + UniquePtr imageReadySemaphore; + UniquePtr renderFinishedSemaphore; + UniquePtr frameFence; +}; + +BaseTexApp::BaseTexApp(const std::string& inName) + : Application(inName) + , swapChainFormat(PixelFormat::max) + , swapChainTextures() +{ +} + +BaseTexApp::~BaseTexApp() = default; + +void BaseTexApp::OnCreate() +{ + CompileAllShaders(); + RenderThread::Get().Start(); + RenderWorkerThreads::Get().Start(); + + CreateDevice(); + CreateSurface(); + + RenderThread::Get().EmplaceTask([this]() -> void { + FetchShaderInstances(); + CreateSwapChain(); + CreateVertexAndIndexBuffer(); + CreateTextureAndSampler(); + CreateSyncObjects(); + }); +} + +void BaseTexApp::OnDrawFrame() +{ + RenderThread::Get().EmplaceTask([this]() -> void { + frameFence->Reset(); + const auto backTextureIndex = swapChain->AcquireBackTexture(imageReadySemaphore.Get()); + + auto* pso = PipelineCache::Get(*device).GetOrCreate( + RasterPipelineStateDesc() + .SetVertexShader(vs) + .SetPixelShader(ps) + .SetVertexState( + RVertexState() + .AddVertexBufferLayout( + RVertexBufferLayout(VertexStepMode::perVertex, sizeof(Vertex)) + .AddAttribute(RVertexAttribute(RVertexBinding("POSITION", 0), VertexFormat::float32X4, offsetof(Vertex, position))) + .AddAttribute(RVertexAttribute(RVertexBinding("TEXCOORD", 0), VertexFormat::float32X2, offsetof(Vertex, uv))))) + .SetFragmentState( + RFragmentState() + .AddColorTarget(ColorTargetState(swapChainFormat, ColorWriteBits::all, false))) + .SetPrimitiveState(PrimitiveState(PrimitiveTopologyType::triangle, FillMode::solid, IndexFormat::uint16, FrontFace::ccw, CullMode::none))); + + RGBuilder builder(*device); + auto* backTexture = builder.ImportTexture(swapChainTextures[backTextureIndex], TextureState::present); + auto* backTextureView = builder.CreateTextureView(backTexture, RGTextureViewDesc(TextureViewType::colorAttachment, TextureViewDimension::tv2D)); + auto* vBuffer = builder.ImportBuffer(vertexBuffer.Get(), BufferState::shaderReadOnly); + auto* vBufferView = builder.CreateBufferView(vBuffer, RGBufferViewDesc(BufferViewType::vertex, vBuffer->GetDesc().size, 0, VertexBufferViewInfo(sizeof(Vertex)))); + auto* iBuffer = builder.ImportBuffer(indexBuffer.Get(), BufferState::shaderReadOnly); + auto* iBufferView = builder.CreateBufferView(iBuffer, RGBufferViewDesc(BufferViewType::index, iBuffer->GetDesc().size, 0, IndexBufferViewInfo(IndexFormat::uint32))); + auto* uBuffer = builder.CreateBuffer(RGBufferDesc(sizeof(VertUniform), BufferUsageBits::uniform | BufferUsageBits::mapWrite, BufferState::staging, "psUniform")); + auto* uBufferView = builder.CreateBufferView(uBuffer, RGBufferViewDesc(BufferViewType::uniformBinding, sizeof(VertUniform))); + auto* rgTexture = builder.ImportTexture(texture.Get(), TextureState::undefined); + auto* rgTextureView = builder.CreateTextureView(rgTexture, RGTextureViewDesc(TextureViewType::textureBinding, TextureViewDimension::tv2D)); + + auto* bindGroup = builder.AllocateBindGroup( + RGBindGroupDesc::Create(pso->GetPipelineLayout()->GetBindGroupLayout(0)) + .UniformBuffer("constantBuffer", uBufferView) + .Sampler("colorSampler", sampler.Get()) + .Texture("colorTex", rgTextureView)); + + VertUniform vertUniform {}; + vertUniform.modelMatrix = MatConsts::identity; + + builder.QueueBufferUpload( + uBuffer, + RGBufferUploadInfo(&vertUniform, sizeof(VertUniform))); + + builder.AddRasterPass( + "BasePass", + RGRasterPassDesc() + .AddColorAttachment(RGColorAttachment(backTextureView, LoadOp::clear, StoreOp::store)), + { bindGroup }, + [pso, vBufferView, iBufferView, bindGroup, viewportWidth = GetWindowWidth(), viewportHeight = GetWindowHeight()](const RGBuilder& rg, RasterPassCommandRecorder& recorder) -> void { + recorder.SetPipeline(pso->GetRHI()); + recorder.SetScissor(0, 0, viewportWidth, viewportHeight); + recorder.SetViewport(0, 0, static_cast(viewportWidth), static_cast(viewportHeight), 0, 1); + recorder.SetVertexBuffer(0, rg.GetRHI(vBufferView)); + recorder.SetIndexBuffer(rg.GetRHI(iBufferView)); + recorder.SetPrimitiveTopology(PrimitiveTopology::triangleList); + recorder.SetBindGroup(0, rg.GetRHI(bindGroup)); + recorder.DrawIndexed(6, 1, 0, 0, 0); + }, + {}, + [backTexture](const RGBuilder& rg, CommandRecorder& recorder) -> void { + recorder.ResourceBarrier(Barrier::Transition(rg.GetRHI(backTexture), TextureState::renderTarget, TextureState::present)); + }); + + RGExecuteInfo executeInfo; + executeInfo.semaphoresToWait = { imageReadySemaphore.Get() }; + executeInfo.semaphoresToSignal = { renderFinishedSemaphore.Get() }; + executeInfo.inFenceToSignal = frameFence.Get(); + builder.Execute(executeInfo); + swapChain->Present(renderFinishedSemaphore.Get()); + frameFence->Wait(); + + Core::ThreadContext::IncFrameNumber(); + BufferPool::Get(*device).Forfeit(); + TexturePool::Get(*device).Forfeit(); + ResourceViewCache::Get(*device).Forfeit(); + BindGroupCache::Get(*device).Forfeit(); + }); + + // TODO in sample, just sync with render thread every frame, maybe later need a better render-thread based application class + RenderThread::Get().Flush(); +} + +void BaseTexApp::OnDestroy() +{ + RenderThread::Get().EmplaceTask([this]() -> void { + const UniquePtr fence = device->CreateFence(false); + device->GetQueue(QueueType::graphics, 0)->Flush(fence.Get()); + fence->Wait(); + + BindGroupCache::Get(*device).Invalidate(); + PipelineCache::Get(*device).Invalidate(); + BufferPool::Get(*device).Invalidate(); + TexturePool::Get(*device).Invalidate(); + ShaderMap::Get(*device).Invalidate(); + }); + RenderThread::Get().Flush(); + + RenderWorkerThreads::Get().Stop(); + RenderThread::Get().Stop(); +} + +void BaseTexApp::CreateDevice() +{ + device = GetRHIInstance() + ->GetGpu(0) + ->RequestDevice( + DeviceCreateInfo() + .AddQueueRequest(QueueRequestInfo(QueueType::graphics, 1))); +} + +void BaseTexApp::CreateSurface() +{ + surface = device->CreateSurface(SurfaceCreateInfo(GetPlatformWindow())); +} + +void BaseTexApp::CompileAllShaders() const +{ + ShaderCompileOptions options; + options.includeDirectories = {"../Test/Sample/ShaderInclude", "../Test/Sample/Rendering-BaseTexture"}; + options.byteCodeType = GetRHIType() == RHI::RHIType::directX12 ? ShaderByteCodeType::dxil : ShaderByteCodeType::spirv; + options.withDebugInfo = false; + auto result = ShaderTypeCompiler::Get().CompileAll(options); + const auto& [success, errorInfo] = result.get(); + Assert(success); +} + +void BaseTexApp::FetchShaderInstances() +{ + ShaderArtifactRegistry::Get().PerformThreadCopy(); + vs = ShaderMap::Get(*device).GetShaderInstance(BaseTexVS::Get(), {}); + ps = ShaderMap::Get(*device).GetShaderInstance(BaseTexPS::Get(), {}); +} + +void BaseTexApp::CreateSwapChain() +{ + static std::vector swapChainFormatQualifiers = { + PixelFormat::rgba8Unorm, + PixelFormat::bgra8Unorm + }; + + surface = device->CreateSurface(SurfaceCreateInfo(GetPlatformWindow())); + + for (const auto format : swapChainFormatQualifiers) { + if (device->CheckSwapChainFormatSupport(surface.Get(), format)) { + swapChainFormat = format; + break; + } + } + Assert(swapChainFormat != PixelFormat::max); + + swapChain = device->CreateSwapChain( + SwapChainCreateInfo() + .SetFormat(swapChainFormat) + .SetPresentMode(PresentMode::immediately) + .SetTextureNum(backBufferCount) + .SetWidth(GetWindowWidth()) + .SetHeight(GetWindowHeight()) + .SetSurface(surface.Get()) + .SetPresentQueue(device->GetQueue(QueueType::graphics, 0))); + + for (auto i = 0; i < backBufferCount; i++) { + swapChainTextures[i] = swapChain->GetTexture(i); + } +} + +void BaseTexApp::CreateVertexAndIndexBuffer() +{ + const std::vector vertices = { + {{-.5f, -.5f, .0f, 1.f}, {.0f, 1.0f}}, + {{.5f, -.5f, .0f,1.f}, {1.0f, 1.0f}}, + {{.5f, .5f, .0f, 1.f}, {1.0f, .0f}}, + {{-.5f, .5f, .0f, 1.f}, {0.f, .0f}}, + }; + + const BufferCreateInfo bufferCreateInfo = BufferCreateInfo() + .SetSize(vertices.size() * sizeof(Vertex)) + .SetUsages(BufferUsageBits::vertex | BufferUsageBits::mapWrite | BufferUsageBits::copySrc) + .SetInitialState(BufferState::staging) + .SetDebugName("vertexBuffer"); + + vertexBuffer = device->CreateBuffer(bufferCreateInfo); + if (vertexBuffer != nullptr) { + auto* data = vertexBuffer->Map(MapMode::write, 0, bufferCreateInfo.size); + memcpy(data, vertices.data(), bufferCreateInfo.size); + vertexBuffer->UnMap(); + } + + const std::vector indices = {0, 1, 2, 0, 2, 3}; + const BufferCreateInfo indexInfo = BufferCreateInfo() + .SetSize(indices.size() * sizeof(uint32_t)) + .SetUsages(BufferUsageBits::index | BufferUsageBits::mapWrite | BufferUsageBits::copySrc) + .SetInitialState(BufferState::staging) + .SetDebugName("indexBuffer"); + + indexBuffer = device->CreateBuffer(indexInfo); + if (indexBuffer != nullptr) { + auto* data = indexBuffer->Map(MapMode::write, 0, indexInfo.size); + memcpy(data, indices.data(), bufferCreateInfo.size); + indexBuffer->UnMap(); + } +} + +void BaseTexApp::CreateTextureAndSampler() +{ + int width, height, channel = 0; + stbi_uc* imgData = stbi_load("../Test/Sample/Rendering-BaseTexture/Awesomeface.png", &width, &height, &channel, STBI_rgb_alpha); + Assert(imgData != nullptr); + + texture = device->CreateTexture( + TextureCreateInfo() + .SetFormat(PixelFormat::rgba8Unorm) + .SetMipLevels(1) + .SetWidth(width) + .SetHeight(height) + .SetDepthOrArraySize(1) + .SetDimension(TextureDimension::t2D) + .SetSamples(1) + .SetUsages(TextureUsageBits::copyDst | TextureUsageBits::textureBinding) + .SetInitialState(TextureState::undefined) + ); + + const auto copyFootprint = device->GetTextureSubResourceCopyFootprint(*texture, TextureSubResourceInfo()); + + const BufferCreateInfo info = BufferCreateInfo() + .SetSize(copyFootprint.totalBytes) + .SetUsages(BufferUsageBits::mapWrite | BufferUsageBits::copySrc) + .SetInitialState(BufferState::staging) + .SetDebugName("texStagingBuffer"); + + imageBuffer = device->CreateBuffer(info); + if (imageBuffer != nullptr) { + auto* data = imageBuffer->Map(MapMode::write, 0, info.size); + for (auto i = 0; i < height; i++) { + const auto srcRowPitch = width * copyFootprint.bytesPerPixel; + const auto* src = imgData + i * srcRowPitch; + auto* dst = static_cast(data) + i * copyFootprint.rowPitch; + memcpy(dst, src, srcRowPitch); + } + imageBuffer->UnMap(); + } + stbi_image_free(imgData); + + sampler = device->CreateSampler(SamplerCreateInfo()); + + // perform buffer->texture copy + auto copyCmdBuffer = device->CreateCommandBuffer(); + const UniquePtr commandRecorder = copyCmdBuffer->Begin(); + { + const UniquePtr copyRecorder = commandRecorder->BeginCopyPass(); + { + copyRecorder->ResourceBarrier(Barrier::Transition(texture.Get(), TextureState::undefined, TextureState::copyDst)); + copyRecorder->CopyBufferToTexture( + imageBuffer.Get(), + texture.Get(), + BufferTextureCopyInfo(0, TextureSubResourceInfo(), UVec3Consts::zero, UVec3(static_cast(width), static_cast(height), 1))); + copyRecorder->ResourceBarrier(Barrier::Transition(texture.Get(), TextureState::copyDst, TextureState::shaderReadOnly)); + } + copyRecorder->EndPass(); + } + commandRecorder->End(); + + const UniquePtr fence = device->CreateFence(false); + QueueSubmitInfo submitInfo {}; + submitInfo.signalFence = fence.Get(); + device->GetQueue(QueueType::graphics, 0)->Submit(copyCmdBuffer.Get(), submitInfo); + fence->Wait(); +} + +void BaseTexApp::CreateSyncObjects() +{ + imageReadySemaphore = device->CreateSemaphore(); + renderFinishedSemaphore = device->CreateSemaphore(); + frameFence = device->CreateFence(true); +} + +int main(int argc, char* argv[]) +{ + BaseTexApp application("Rendering-BaseTex"); + if (!application.Initialize(argc, argv)) { + return -1; + } + return application.RunLoop(); +} diff --git a/Sample/Rendering-BaseTexture/BaseTexture.esl b/Sample/Rendering-BaseTexture/BaseTexture.esl new file mode 100644 index 00000000..0e0edde4 --- /dev/null +++ b/Sample/Rendering-BaseTexture/BaseTexture.esl @@ -0,0 +1,31 @@ +#include + +VkBinding(0, 0) Texture2D colorTex : register(t0); +VkBinding(1, 0) SamplerState colorSampler : register(s0); +VkBinding(2, 0) cbuffer constantBuffer : register(b0) +{ + float4x4 model; +}; + +struct FragmentInput { + float4 position : SV_POSITION; + float2 uv : TEXCOORD; +}; + +FragmentInput VSMain( + VkLocation(0) float4 position : POSITION, + VkLocation(1) float2 uv : TEXCOORD) +{ + FragmentInput fragmentInput; + fragmentInput.position = mul(model, position); + fragmentInput.uv = uv; +#if VULKAN + fragmentInput.uv.y = 1 - fragmentInput.uv.y; +#endif + return fragmentInput; +} + +float4 PSMain(FragmentInput input) : SV_TARGET +{ + return colorTex.Sample(colorSampler, input.uv); +}