diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/gfx/d3d-util.cpp | 1 | ||||
| -rw-r--r-- | tools/gfx/gfx.vcxproj | 10 | ||||
| -rw-r--r-- | tools/gfx/gfx.vcxproj.filters | 6 | ||||
| -rw-r--r-- | tools/gfx/gui.cpp | 398 | ||||
| -rw-r--r-- | tools/gfx/gui.h | 28 | ||||
| -rw-r--r-- | tools/gfx/render-d3d11.cpp | 193 | ||||
| -rw-r--r-- | tools/gfx/render-d3d12.cpp | 44 | ||||
| -rw-r--r-- | tools/gfx/render-gl.cpp | 42 | ||||
| -rw-r--r-- | tools/gfx/render-vk.cpp | 46 | ||||
| -rw-r--r-- | tools/gfx/render.cpp | 1 | ||||
| -rw-r--r-- | tools/gfx/render.h | 106 | ||||
| -rw-r--r-- | tools/gfx/window.cpp | 33 |
12 files changed, 904 insertions, 4 deletions
diff --git a/tools/gfx/d3d-util.cpp b/tools/gfx/d3d-util.cpp index 19135707b..f479af186 100644 --- a/tools/gfx/d3d-util.cpp +++ b/tools/gfx/d3d-util.cpp @@ -31,6 +31,7 @@ using namespace Slang; case Format::RG_Float32: return DXGI_FORMAT_R32G32_FLOAT; case Format::R_Float32: return DXGI_FORMAT_R32_FLOAT; case Format::RGBA_Unorm_UInt8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case Format::R_UInt16: return DXGI_FORMAT_R16_UINT; case Format::R_UInt32: return DXGI_FORMAT_R32_UINT; case Format::D_Float32: return DXGI_FORMAT_D32_FLOAT; diff --git a/tools/gfx/gfx.vcxproj b/tools/gfx/gfx.vcxproj index cbafe84b1..6de379c73 100644 --- a/tools/gfx/gfx.vcxproj +++ b/tools/gfx/gfx.vcxproj @@ -95,7 +95,7 @@ <PrecompiledHeader>NotUsing</PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories>..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..;..\..\external;..\..\source;..\..\external\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> <Optimization>Disabled</Optimization> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> @@ -113,7 +113,7 @@ <PrecompiledHeader>NotUsing</PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories>..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..;..\..\external;..\..\source;..\..\external\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> <Optimization>Disabled</Optimization> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> @@ -131,7 +131,7 @@ <PrecompiledHeader>NotUsing</PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories>..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..;..\..\external;..\..\source;..\..\external\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <Optimization>Full</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> @@ -153,7 +153,7 @@ <PrecompiledHeader>NotUsing</PrecompiledHeader> <WarningLevel>Level3</WarningLevel> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories>..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..;..\..\external;..\..\source;..\..\external\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <Optimization>Full</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> @@ -174,6 +174,7 @@ <ClInclude Include="circular-resource-heap-d3d12.h" /> <ClInclude Include="d3d-util.h" /> <ClInclude Include="descriptor-heap-d3d12.h" /> + <ClInclude Include="gui.h" /> <ClInclude Include="model.h" /> <ClInclude Include="render-d3d11.h" /> <ClInclude Include="render-d3d12.h" /> @@ -194,6 +195,7 @@ <ClCompile Include="circular-resource-heap-d3d12.cpp" /> <ClCompile Include="d3d-util.cpp" /> <ClCompile Include="descriptor-heap-d3d12.cpp" /> + <ClCompile Include="gui.cpp" /> <ClCompile Include="model.cpp" /> <ClCompile Include="render-d3d11.cpp" /> <ClCompile Include="render-d3d12.cpp" /> diff --git a/tools/gfx/gfx.vcxproj.filters b/tools/gfx/gfx.vcxproj.filters index f1c7f7f5e..8164d45b0 100644 --- a/tools/gfx/gfx.vcxproj.filters +++ b/tools/gfx/gfx.vcxproj.filters @@ -18,6 +18,9 @@ <ClInclude Include="descriptor-heap-d3d12.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="gui.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="model.h"> <Filter>Header Files</Filter> </ClInclude> @@ -74,6 +77,9 @@ <ClCompile Include="descriptor-heap-d3d12.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="gui.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="model.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/tools/gfx/gui.cpp b/tools/gfx/gui.cpp new file mode 100644 index 000000000..85a080526 --- /dev/null +++ b/tools/gfx/gui.cpp @@ -0,0 +1,398 @@ +// gui.cpp +#include "gui.h" + +#ifdef _WIN32 +#include <windows.h> +#include "external/imgui/examples/imgui_impl_win32.h" +IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +#endif + + +namespace gfx { + +#ifdef _WIN32 +LRESULT CALLBACK guiWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam); +} +void setNativeWindowHook(Window* window, WNDPROC proc); +#endif + + +GUI::GUI(Window* window, Renderer* inRenderer) + : renderer(inRenderer) +{ + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + +#ifdef _WIN32 + ImGui_ImplWin32_Init(getPlatformWindowHandle(window)); + + setNativeWindowHook(window, &guiWindowProc); +#endif + + // Let's do the initialization work required for our graphics API + // abstraction layer, so that we can pipe all IMGUI rendering + // through the same interface as other work. + // + + static const char* shaderCode = + "cbuffer U { float4x4 mvp; }; \ + Texture2D t; \ + SamplerState s; \ + struct AssembledVertex { \ + float2 pos; \ + float2 uv; \ + float4 col; \ + }; \ + struct CoarseVertex { \ + float4 col; \ + float2 uv; \ + }; \ + struct VSOutput { \ + CoarseVertex cv : U; \ + float4 pos : SV_Position; \ + }; \ + void vertexMain( \ + AssembledVertex i : U, \ + out VSOutput o) \ + { \ + o.cv.col = i.col; \ + o.cv.uv = i.uv; \ + o.pos = mul(mvp, \ + float4(i.pos.xy, 0.f, 1.f)); \ + } \ + float4 fragmentMain( \ + CoarseVertex i : U) \ + : SV_target \ + { \ + return i.col * t.Sample(s, i.uv); \ + } \ + "; + + SlangSession* slangSession = spCreateSession(nullptr); + SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession); + + // TODO: These two lines need to change based on what the target graphics API + // is, so we need a way for a `Renderer` to pass back its prefeerred code + // format and profile name... + // + int targetIndex = spAddCodeGenTarget(slangRequest, SLANG_DXBC); + spSetTargetProfile(slangRequest, targetIndex, spFindProfile(slangSession, "sm_4_0")); + + int translationUnitIndex = spAddTranslationUnit(slangRequest, SLANG_SOURCE_LANGUAGE_SLANG, nullptr); + spAddTranslationUnitSourceString(slangRequest, translationUnitIndex, "gui.cpp.slang", shaderCode); + + char const* vertexEntryPointName = "vertexMain"; + char const* fragmentEntryPointName = "fragmentMain"; + int vertexIndex = spAddEntryPoint(slangRequest, translationUnitIndex, vertexEntryPointName, SLANG_STAGE_VERTEX); + int fragmentIndex = spAddEntryPoint(slangRequest, translationUnitIndex, fragmentEntryPointName, SLANG_STAGE_FRAGMENT); + + const SlangResult compileRes = spCompile(slangRequest); + if(auto diagnostics = spGetDiagnosticOutput(slangRequest)) + { + reportError("%s", diagnostics); + } + if(SLANG_FAILED(compileRes)) + { + spDestroyCompileRequest(slangRequest); + spDestroySession(slangSession); + assert(!"unexpected"); + return; + } + + ISlangBlob* vertexShaderBlob = nullptr; + spGetEntryPointCodeBlob(slangRequest, vertexIndex, 0, &vertexShaderBlob); + + ISlangBlob* fragmentShaderBlob = nullptr; + spGetEntryPointCodeBlob(slangRequest, fragmentIndex, 0, &fragmentShaderBlob); + + char const* vertexCode = (char const*) vertexShaderBlob->getBufferPointer(); + char const* vertexCodeEnd = vertexCode + vertexShaderBlob->getBufferSize(); + + char const* fragmentCode = (char const*) fragmentShaderBlob->getBufferPointer(); + char const* fragmentCodeEnd = fragmentCode + fragmentShaderBlob->getBufferSize(); + + spDestroyCompileRequest(slangRequest); + spDestroySession(slangSession); + + gfx::ShaderProgram::KernelDesc kernelDescs[] = + { + { gfx::StageType::Vertex, vertexCode, vertexCodeEnd }, + { gfx::StageType::Fragment, fragmentCode, fragmentCodeEnd }, + }; + + gfx::ShaderProgram::Desc programDesc; + programDesc.pipelineType = gfx::PipelineType::Graphics; + programDesc.kernels = &kernelDescs[0]; + programDesc.kernelCount = 2; + + auto program = renderer->createProgram(programDesc); + + vertexShaderBlob->release(); + fragmentShaderBlob->release(); + + InputElementDesc inputElements[] = { + {"U", 0, Format::RG_Float32, offsetof(ImDrawVert, pos) }, + {"U", 1, Format::RG_Float32, offsetof(ImDrawVert, uv) }, + {"U", 2, Format::RGBA_Unorm_UInt8, offsetof(ImDrawVert, col) }, + }; + auto inputLayout = renderer->createInputLayout( + &inputElements[0], + SLANG_COUNT_OF(inputElements)); + + // + + List<DescriptorSetLayout::SlotRangeDesc> descriptorSetRanges; + descriptorSetRanges.Add(DescriptorSetLayout::SlotRangeDesc(DescriptorSlotType::UniformBuffer)); + descriptorSetRanges.Add(DescriptorSetLayout::SlotRangeDesc(DescriptorSlotType::SampledImage)); + descriptorSetRanges.Add(DescriptorSetLayout::SlotRangeDesc(DescriptorSlotType::Sampler)); + + DescriptorSetLayout::Desc descriptorSetLayoutDesc; + descriptorSetLayoutDesc.slotRangeCount = descriptorSetRanges.Count(); + descriptorSetLayoutDesc.slotRanges = descriptorSetRanges.Buffer(); + + descriptorSetLayout = renderer->createDescriptorSetLayout(descriptorSetLayoutDesc); + + List<PipelineLayout::DescriptorSetDesc> pipelineDescriptorSets; + pipelineDescriptorSets.Add(PipelineLayout::DescriptorSetDesc(descriptorSetLayout)); + + PipelineLayout::Desc pipelineLayoutDesc; + pipelineLayoutDesc.descriptorSetCount = pipelineDescriptorSets.Count(); + pipelineLayoutDesc.descriptorSets = pipelineDescriptorSets.Buffer(); + pipelineLayoutDesc.renderTargetCount = 1; + + pipelineLayout = renderer->createPipelineLayout(pipelineLayoutDesc); + + TargetBlendDesc targetBlendDesc; + targetBlendDesc.color.srcFactor = BlendFactor::SrcAlpha; + targetBlendDesc.color.dstFactor = BlendFactor::InvSrcAlpha; + targetBlendDesc.alpha.srcFactor = BlendFactor::InvSrcAlpha; + targetBlendDesc.alpha.dstFactor = BlendFactor::Zero; + + GraphicsPipelineStateDesc pipelineDesc; + pipelineDesc.renderTargetCount = 1; + pipelineDesc.program = program; + pipelineDesc.pipelineLayout = pipelineLayout; + pipelineDesc.inputLayout = inputLayout; + pipelineDesc.blend.targets = &targetBlendDesc; + pipelineDesc.blend.targetCount = 1; + pipelineDesc.rasterizer.cullMode = CullMode::None; + + // Set up the pieces of fixed-function state that we care about + pipelineDesc.depthStencil.depthTestEnable = false; + + // TODO: need to set up blending state... + + pipelineState = renderer->createGraphicsPipelineState(pipelineDesc); + + // Initialize the texture atlas + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + + { + gfx::TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::RGBA_Unorm_UInt8, width, height, 1); + desc.setDefaults(Resource::Usage::PixelShaderResource); + + + ptrdiff_t mipRowStrides[] = { ptrdiff_t(width * 4 * sizeof(unsigned char)) }; + void* subResourceData[] = { pixels }; + TextureResource::Data initData; + initData.mipRowStrides = mipRowStrides; + initData.numMips = 1; + initData.numSubResources = 1; + initData.subResources = subResourceData; + + auto texture = renderer->createTextureResource(Resource::Usage::PixelShaderResource, desc, &initData); + + gfx::ResourceView::Desc viewDesc; + viewDesc.format = desc.format; + viewDesc.type = ResourceView::Type::ShaderResource; + auto textureView = renderer->createTextureView(texture, viewDesc); + + io.Fonts->TexID = (void*) textureView.detach(); + } + + { + SamplerState::Desc desc; + samplerState = renderer->createSamplerState(desc); + } +} + + + +void GUI::beginFrame() +{ +#ifdef _WIN32 + ImGui_ImplWin32_NewFrame(); +#endif + ImGui::NewFrame(); +} + +void GUI::endFrame() +{ + ImGui::Render(); + + ImDrawData* draw_data = ImGui::GetDrawData(); + auto vertexCount = draw_data->TotalVtxCount; + auto indexCount = draw_data->TotalIdxCount; + int commandListCount = draw_data->CmdListsCount; + + if(!vertexCount) return; + if(!indexCount) return; + if(!commandListCount) return; + + // Allocate transient vertex/index buffers to hold the data for this frame. + + gfx::BufferResource::Desc vertexBufferDesc; + vertexBufferDesc.init(vertexCount * sizeof(ImDrawVert)); + vertexBufferDesc.setDefaults(Resource::Usage::VertexBuffer); + vertexBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; + auto vertexBuffer = renderer->createBufferResource( + Resource::Usage::VertexBuffer, + vertexBufferDesc); + + gfx::BufferResource::Desc indexBufferDesc; + indexBufferDesc.init(indexCount * sizeof(ImDrawIdx)); + indexBufferDesc.setDefaults(Resource::Usage::IndexBuffer); + indexBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; + auto indexBuffer = renderer->createBufferResource( + Resource::Usage::IndexBuffer, + indexBufferDesc); + + { + ImDrawVert* dstVertex = (ImDrawVert*) renderer->map(vertexBuffer, MapFlavor::WriteDiscard); + ImDrawIdx* dstIndex = (ImDrawIdx*) renderer->map(indexBuffer, MapFlavor::WriteDiscard); + + for(int ii = 0; ii < commandListCount; ++ii) + { + const ImDrawList* commandList = draw_data->CmdLists[ii]; + memcpy(dstVertex, commandList->VtxBuffer.Data, commandList->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(dstIndex, commandList->IdxBuffer.Data, commandList->IdxBuffer.Size * sizeof(ImDrawIdx)); + dstVertex += commandList->VtxBuffer.Size; + dstIndex += commandList->IdxBuffer.Size; + } + + renderer->unmap(vertexBuffer); + renderer->unmap(indexBuffer); + } + + // Allocate a transient constant buffer for projection matrix + gfx::BufferResource::Desc constantBufferDesc; + constantBufferDesc.init(sizeof(glm::mat4x4)); + constantBufferDesc.setDefaults(Resource::Usage::ConstantBuffer); + constantBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; + auto constantBuffer = renderer->createBufferResource( + Resource::Usage::ConstantBuffer, + constantBufferDesc); + + { + glm::mat4x4* dstMVP = (glm::mat4x4*) renderer->map(constantBuffer, MapFlavor::WriteDiscard); + + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + float mvp[4][4] = + { + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.5f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, + }; + memcpy(dstMVP, mvp, sizeof(mvp)); + + renderer->unmap(constantBuffer); + } + + gfx::Viewport viewport; + viewport.originX = 0; + viewport.originY = 0; + viewport.extentY = draw_data->DisplaySize.y; + viewport.extentX = draw_data->DisplaySize.x; + viewport.extentY = draw_data->DisplaySize.y; + viewport.minZ = 0; + viewport.maxZ = 0; + + renderer->setViewport(viewport); + + auto pipelineType = PipelineType::Graphics; + renderer->setPipelineState(pipelineType, pipelineState); + + renderer->setVertexBuffer(0, vertexBuffer, sizeof(ImDrawVert)); + renderer->setIndexBuffer(indexBuffer, sizeof(ImDrawIdx) == 2 ? Format::R_UInt16 : Format::R_UInt32); + renderer->setPrimitiveTopology(PrimitiveTopology::TriangleList); + + UInt vertexOffset = 0; + UInt indexOffset = 0; + ImVec2 pos = draw_data->DisplayPos; + for(int ii = 0; ii < commandListCount; ++ii) + { + auto commandList = draw_data->CmdLists[ii]; + auto commandCount = commandList->CmdBuffer.Size; + for(int jj = 0; jj < commandCount; jj++) + { + auto command = &commandList->CmdBuffer[jj]; + if(auto userCallback = command->UserCallback) + { + userCallback(commandList, command); + } + else + { + ScissorRect rect = + { + (Int)(command->ClipRect.x - pos.x), + (Int)(command->ClipRect.y - pos.y), + (Int)(command->ClipRect.z - pos.x), + (Int)(command->ClipRect.w - pos.y) + }; + renderer->setScissorRect(rect); + + // TODO: This should be a dynamic/transient descriptor set... + auto descriptorSet = renderer->createDescriptorSet(descriptorSetLayout); + descriptorSet->setConstantBuffer(0, 0, constantBuffer); + descriptorSet->setResource(1, 0, + (gfx::ResourceView*) command->TextureId); + descriptorSet->setSampler(2, 0, + samplerState); + + renderer->setDescriptorSet( + pipelineType, + pipelineLayout, + 0, + descriptorSet); + + renderer->drawIndexed(command->ElemCount, indexOffset, vertexOffset); + } + indexOffset += command->ElemCount; + } + vertexOffset += commandList->VtxBuffer.Size; + } +} + +GUI::~GUI() +{ + auto& io = ImGui::GetIO(); + + { + RefPtr<ResourceView> textureView; + textureView.attach((ResourceView*) io.Fonts->TexID); + textureView = nullptr; + } + +#ifdef _WIN32 + ImGui_ImplWin32_Shutdown(); +#endif + + ImGui::DestroyContext(); +} + +} // gfx + +#include "external/imgui/imgui.cpp" +#include "external/imgui/imgui_draw.cpp" +#ifdef _WIN32 +#include "external/imgui/examples/imgui_impl_win32.cpp" +#endif diff --git a/tools/gfx/gui.h b/tools/gfx/gui.h new file mode 100644 index 000000000..cbec5796f --- /dev/null +++ b/tools/gfx/gui.h @@ -0,0 +1,28 @@ +// gui.h +#pragma once + +#include "render.h" +#include "vector-math.h" +#include "window.h" + +#include "external/imgui/imgui.h" + +namespace gfx { + +struct GUI : RefObject +{ + GUI(Window* window, Renderer* renderer); + ~GUI(); + + void beginFrame(); + void endFrame(); + +private: + RefPtr<Renderer> renderer; + RefPtr<PipelineState> pipelineState; + RefPtr<DescriptorSetLayout> descriptorSetLayout; + RefPtr<PipelineLayout> pipelineLayout; + RefPtr<SamplerState> samplerState; +}; + +} // gfx diff --git a/tools/gfx/render-d3d11.cpp b/tools/gfx/render-d3d11.cpp index 57c0672bd..7bf712216 100644 --- a/tools/gfx/render-d3d11.cpp +++ b/tools/gfx/render-d3d11.cpp @@ -86,6 +86,8 @@ public: virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + void setViewports(UInt count, Viewport const* viewports) override; + void setScissorRects(UInt count, ScissorRect const* rects) override; virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; virtual void draw(UInt vertexCount, UInt startVertex) override; virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; @@ -282,8 +284,11 @@ public: RefPtr<InputLayoutImpl> m_inputLayout; ComPtr<ID3D11DepthStencilState> m_depthStencilState; ComPtr<ID3D11RasterizerState> m_rasterizerState; + ComPtr<ID3D11BlendState> m_blendState; UINT m_stencilRef; + float m_blendColor[4]; + UINT m_sampleMask; }; class ComputePipelineStateImpl : public PipelineStateImpl @@ -1111,6 +1116,7 @@ Result D3D11Renderer::createInputLayout(const InputElementDesc* inputElementsIn, switch (inputElementsIn[ii].format) { case Format::RGBA_Float32: + case Format::RGBA_Unorm_UInt8: typeName = "float4"; break; case Format::RGB_Float32: @@ -1245,6 +1251,49 @@ void D3D11Renderer::setDepthStencilTarget(ResourceView* depthStencilView) m_targetBindingsDirty[int(PipelineType::Graphics)] = true; } +void D3D11Renderer::setViewports(UInt count, Viewport const* viewports) +{ + static const int kMaxViewports = D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX + 1; + assert(count <= kMaxViewports); + + D3D11_VIEWPORT dxViewports[kMaxViewports]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inViewport = viewports[ii]; + auto& dxViewport = dxViewports[ii]; + + dxViewport.TopLeftX = inViewport.originX; + dxViewport.TopLeftY = inViewport.originY; + dxViewport.Width = inViewport.extentX; + dxViewport.Height = inViewport.extentY; + dxViewport.MinDepth = inViewport.minZ; + dxViewport.MaxDepth = inViewport.maxZ; + } + + m_immediateContext->RSSetViewports(count, dxViewports); +} + +void D3D11Renderer::setScissorRects(UInt count, ScissorRect const* rects) +{ + static const int kMaxScissorRects = D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX + 1; + assert(count <= kMaxScissorRects); + + D3D11_RECT dxRects[kMaxScissorRects]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inRect = rects[ii]; + auto& dxRect = dxRects[ii]; + + dxRect.left = inRect.minX; + dxRect.top = inRect.minY; + dxRect.right = inRect.maxX; + dxRect.bottom = inRect.maxY; + } + + m_immediateContext->RSSetScissorRects(count, dxRects); +} + + void D3D11Renderer::setPipelineState(PipelineType pipelineType, PipelineState* state) { switch(pipelineType) @@ -1288,6 +1337,7 @@ void D3D11Renderer::setPipelineState(PipelineType pipelineType, PipelineState* s // OM + m_immediateContext->OMSetBlendState(stateImpl->m_blendState, stateImpl->m_blendColor, stateImpl->m_sampleMask); m_immediateContext->OMSetDepthStencilState(stateImpl->m_depthStencilState, stateImpl->m_stencilRef); m_currentGraphicsState = stateImpl; @@ -1409,6 +1459,82 @@ static D3D11_CULL_MODE translateCullMode(CullMode mode) } } +bool isBlendDisabled(AspectBlendDesc const& desc) +{ + return desc.op == BlendOp::Add + && desc.srcFactor == BlendFactor::One + && desc.dstFactor == BlendFactor::Zero; +} + + +bool isBlendDisabled(TargetBlendDesc const& desc) +{ + return isBlendDisabled(desc.color) + && isBlendDisabled(desc.alpha); +} + +D3D11_BLEND_OP translateBlendOp(BlendOp op) +{ + switch(op) + { + default: + assert(!"unimplemented"); + return (D3D11_BLEND_OP) -1; + +#define CASE(FROM, TO) case BlendOp::FROM: return D3D11_BLEND_OP_##TO + CASE(Add, ADD); + CASE(Subtract, SUBTRACT); + CASE(ReverseSubtract, REV_SUBTRACT); + CASE(Min, MIN); + CASE(Max, MAX); +#undef CASE + } +} + +D3D11_BLEND translateBlendFactor(BlendFactor factor) +{ + switch(factor) + { + default: + assert(!"unimplemented"); + return (D3D11_BLEND) -1; + +#define CASE(FROM, TO) case BlendFactor::FROM: return D3D11_BLEND_##TO + CASE(Zero, ZERO); + CASE(One, ONE); + CASE(SrcColor, SRC_COLOR); + CASE(InvSrcColor, INV_SRC_COLOR); + CASE(SrcAlpha, SRC_ALPHA); + CASE(InvSrcAlpha, INV_SRC_ALPHA); + CASE(DestAlpha, DEST_ALPHA); + CASE(InvDestAlpha, INV_DEST_ALPHA); + CASE(DestColor, DEST_COLOR); + CASE(InvDestColor, INV_DEST_ALPHA); + CASE(SrcAlphaSaturate, SRC_ALPHA_SAT); + CASE(BlendColor, BLEND_FACTOR); + CASE(InvBlendColor, INV_BLEND_FACTOR); + CASE(SecondarySrcColor, SRC1_COLOR); + CASE(InvSecondarySrcColor, INV_SRC1_COLOR); + CASE(SecondarySrcAlpha, SRC1_ALPHA); + CASE(InvSecondarySrcAlpha, INV_SRC1_ALPHA); +#undef CASE + } +} + +D3D11_COLOR_WRITE_ENABLE translateRenderTargetWriteMask(RenderTargetWriteMaskT mask) +{ + UINT result = 0; +#define CASE(FROM, TO) if(mask & RenderTargetWriteMask::Enable##FROM) result |= D3D11_COLOR_WRITE_ENABLE_##TO + + CASE(Red, RED); + CASE(Green, GREEN); + CASE(Blue, BLUE); + CASE(Alpha, ALPHA); + +#undef CASE + return D3D11_COLOR_WRITE_ENABLE(result); +} + Result D3D11Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) { auto programImpl = (ShaderProgramImpl*) desc.program; @@ -1458,14 +1584,81 @@ Result D3D11Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDes } + ComPtr<ID3D11BlendState> blendState; + { + auto& srcDesc = desc.blend; + D3D11_BLEND_DESC dstDesc = {}; + + TargetBlendDesc defaultTargetBlendDesc; + + static const UInt kMaxTargets = D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; + if(srcDesc.targetCount > kMaxTargets) return SLANG_FAIL; + + for(UInt ii = 0; ii < kMaxTargets; ++ii) + { + TargetBlendDesc const* srcTargetBlendDescPtr = nullptr; + if(ii < srcDesc.targetCount) + { + srcTargetBlendDescPtr = &srcDesc.targets[ii]; + } + else if(srcDesc.targetCount == 0) + { + srcTargetBlendDescPtr = &defaultTargetBlendDesc; + } + else + { + srcTargetBlendDescPtr = &srcDesc.targets[srcDesc.targetCount-1]; + } + + auto& srcTargetBlendDesc = *srcTargetBlendDescPtr; + auto& dstTargetBlendDesc = dstDesc.RenderTarget[ii]; + + if(isBlendDisabled(srcTargetBlendDesc)) + { + dstTargetBlendDesc.BlendEnable = false; + dstTargetBlendDesc.BlendOp = D3D11_BLEND_OP_ADD; + dstTargetBlendDesc.BlendOpAlpha = D3D11_BLEND_OP_ADD; + dstTargetBlendDesc.SrcBlend = D3D11_BLEND_ONE; + dstTargetBlendDesc.SrcBlendAlpha = D3D11_BLEND_ONE; + dstTargetBlendDesc.DestBlend = D3D11_BLEND_ZERO; + dstTargetBlendDesc.DestBlendAlpha = D3D11_BLEND_ZERO; + } + else + { + dstTargetBlendDesc.BlendEnable = true; + dstTargetBlendDesc.BlendOp = translateBlendOp(srcTargetBlendDesc.color.op); + dstTargetBlendDesc.BlendOpAlpha = translateBlendOp(srcTargetBlendDesc.alpha.op); + dstTargetBlendDesc.SrcBlend = translateBlendFactor(srcTargetBlendDesc.color.srcFactor); + dstTargetBlendDesc.SrcBlendAlpha = translateBlendFactor(srcTargetBlendDesc.alpha.srcFactor); + dstTargetBlendDesc.DestBlend = translateBlendFactor(srcTargetBlendDesc.color.dstFactor); + dstTargetBlendDesc.DestBlendAlpha = translateBlendFactor(srcTargetBlendDesc.alpha.dstFactor); + } + + dstTargetBlendDesc.RenderTargetWriteMask = translateRenderTargetWriteMask(srcTargetBlendDesc.writeMask); + } + + dstDesc.IndependentBlendEnable = srcDesc.targetCount > 1; + dstDesc.AlphaToCoverageEnable = srcDesc.alphaToCoverateEnable; + + SLANG_RETURN_ON_FAIL(m_device->CreateBlendState( + &dstDesc, + blendState.writeRef())); + } + RefPtr<GraphicsPipelineStateImpl> state = new GraphicsPipelineStateImpl(); state->m_program = programImpl; state->m_stencilRef = desc.depthStencil.stencilRef; state->m_depthStencilState = depthStencilState; state->m_rasterizerState = rasterizerState; + state->m_blendState = blendState; state->m_pipelineLayout = (PipelineLayoutImpl*) desc.pipelineLayout; state->m_inputLayout = (InputLayoutImpl*) desc.inputLayout; state->m_rtvCount = desc.renderTargetCount; + state->m_blendColor[0] = 0; + state->m_blendColor[1] = 0; + state->m_blendColor[2] = 0; + state->m_blendColor[3] = 0; + state->m_sampleMask = 0xFFFFFFFF; *outState = state.detach(); return SLANG_OK; diff --git a/tools/gfx/render-d3d12.cpp b/tools/gfx/render-d3d12.cpp index 2d3b8f521..38ecaa5ef 100644 --- a/tools/gfx/render-d3d12.cpp +++ b/tools/gfx/render-d3d12.cpp @@ -88,6 +88,8 @@ public: virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + void setViewports(UInt count, Viewport const* viewports) override; + void setScissorRects(UInt count, ScissorRect const* rects) override; virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; virtual void draw(UInt vertexCount, UInt startVertex) override; virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; @@ -2545,6 +2547,48 @@ void D3D12Renderer::setDepthStencilTarget(ResourceView* depthStencilView) { } +void D3D12Renderer::setViewports(UInt count, Viewport const* viewports) +{ + static const int kMaxViewports = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; + assert(count <= kMaxViewports); + + D3D12_VIEWPORT dxViewports[kMaxViewports]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inViewport = viewports[ii]; + auto& dxViewport = dxViewports[ii]; + + dxViewport.TopLeftX = inViewport.originX; + dxViewport.TopLeftY = inViewport.originY; + dxViewport.Width = inViewport.extentX; + dxViewport.Height = inViewport.extentY; + dxViewport.MinDepth = inViewport.minZ; + dxViewport.MaxDepth = inViewport.maxZ; + } + + m_commandList->RSSetViewports(count, dxViewports); +} + +void D3D12Renderer::setScissorRects(UInt count, ScissorRect const* rects) +{ + static const int kMaxScissorRects = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; + assert(count <= kMaxScissorRects); + + D3D12_RECT dxRects[kMaxScissorRects]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inRect = rects[ii]; + auto& dxRect = dxRects[ii]; + + dxRect.left = inRect.minX; + dxRect.top = inRect.minY; + dxRect.right = inRect.maxX; + dxRect.bottom = inRect.maxY; + } + + m_commandList->RSSetScissorRects(count, dxRects); +} + void D3D12Renderer::setPipelineState(PipelineType pipelineType, PipelineState* state) { m_currentPipelineState = (PipelineStateImpl*)state; diff --git a/tools/gfx/render-gl.cpp b/tools/gfx/render-gl.cpp index 3ab818fdd..d77271702 100644 --- a/tools/gfx/render-gl.cpp +++ b/tools/gfx/render-gl.cpp @@ -114,6 +114,8 @@ public: virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + void setViewports(UInt count, Viewport const* viewports) override; + void setScissorRects(UInt count, ScissorRect const* rects) override; virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; virtual void draw(UInt vertexCount, UInt startVertex) override; virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; @@ -1030,6 +1032,46 @@ void GLRenderer::setDepthStencilTarget(ResourceView* depthStencilView) { } +void GLRenderer::setViewports(UInt count, Viewport const* viewports) +{ + assert(count == 1); + auto viewport = viewports[0]; + glViewport( + (GLint) viewport.originX, + (GLint) viewport.originY, + (GLsizei) viewport.extentX, + (GLsizei) viewport.extentY); + glDepthRange(viewport.minZ, viewport.maxZ); +} + +void GLRenderer::setScissorRects(UInt count, ScissorRect const* rects) +{ + assert(count <= 1); + if( count ) + { + // TODO: this isn't goign to be quite right because of the + // flipped coordinate system in GL. + // + // The best way around this is probably to *always* render + // things internally into textures with "flipped" conventions, + // and then only deal with the flipping as part of a final + // "present" step that copies to the primary back-buffer. + // + auto rect = rects[0]; + glScissor( + rect.minX, + rect.minY, + rect.maxX - rect.minX, + rect.maxY - rect.minY); + + glEnable(GL_SCISSOR_TEST); + } + else + { + glDisable(GL_SCISSOR_TEST); + } +} + void GLRenderer::setPipelineState(PipelineType pipelineType, PipelineState* state) { auto pipelineStateImpl = (PipelineStateImpl*) state; diff --git a/tools/gfx/render-vk.cpp b/tools/gfx/render-vk.cpp index 27926e0e6..aee45a86e 100644 --- a/tools/gfx/render-vk.cpp +++ b/tools/gfx/render-vk.cpp @@ -75,6 +75,8 @@ public: virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + void setViewports(UInt count, Viewport const* viewports) override; + void setScissorRects(UInt count, ScissorRect const* rects) override; virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; virtual void draw(UInt vertexCount, UInt startVertex) override; virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; @@ -1877,6 +1879,50 @@ void VKRenderer::setDepthStencilTarget(ResourceView* depthStencilView) { } +void VKRenderer::setViewports(UInt count, Viewport const* viewports) +{ + static const int kMaxViewports = 8; // TODO: base on device caps + assert(count <= kMaxViewports); + + VkViewport vkViewports[kMaxViewports]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inViewport = viewports[ii]; + auto& vkViewport = vkViewports[ii]; + + vkViewport.x = inViewport.originX; + vkViewport.y = inViewport.originY; + vkViewport.width = inViewport.extentX; + vkViewport.height = inViewport.extentY; + vkViewport.minDepth = inViewport.minZ; + vkViewport.maxDepth = inViewport.maxZ; + } + + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + m_api.vkCmdSetViewport(commandBuffer, 0, count, vkViewports); +} + +void VKRenderer::setScissorRects(UInt count, ScissorRect const* rects) +{ + static const int kMaxScissorRects = 8; // TODO: base on device caps + assert(count <= kMaxScissorRects); + + VkRect2D vkRects[kMaxScissorRects]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inRect = rects[ii]; + auto& vkRect = vkRects[ii]; + + vkRect.offset.x = inRect.minX; + vkRect.offset.y = inRect.minY; + vkRect.extent.width = inRect.maxX - inRect.minX; + vkRect.extent.height = inRect.maxY - inRect.minY; + } + + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + m_api.vkCmdSetScissor(commandBuffer, 0, count, vkRects); +} + void VKRenderer::setPipelineState(PipelineType pipelineType, PipelineState* state) { m_currentPipeline = (PipelineStateImpl*)state; diff --git a/tools/gfx/render.cpp b/tools/gfx/render.cpp index 8f887b491..38af85f79 100644 --- a/tools/gfx/render.cpp +++ b/tools/gfx/render.cpp @@ -55,6 +55,7 @@ const Resource::DescBase& Resource::getDescBase() const uint8_t(sizeof(uint32_t)), // RGBA_Unorm_UInt8, + uint8_t(sizeof(uint16_t)), // R_UInt16, uint8_t(sizeof(uint32_t)), // R_UInt32, uint8_t(sizeof(float)), // D_Float32, diff --git a/tools/gfx/render.h b/tools/gfx/render.h index b43152b68..f59a5ef9f 100644 --- a/tools/gfx/render.h +++ b/tools/gfx/render.h @@ -155,6 +155,7 @@ enum class Format RGBA_Unorm_UInt8, + R_UInt16, R_UInt32, D_Float32, @@ -647,6 +648,80 @@ struct RasterizerDesc bool antialiasedLineEnable = false; }; +enum class LogicOp +{ + NoOp, +}; + +enum class BlendOp +{ + Add, + Subtract, + ReverseSubtract, + Min, + Max, +}; + +enum class BlendFactor +{ + Zero, + One, + SrcColor, + InvSrcColor, + SrcAlpha, + InvSrcAlpha, + DestAlpha, + InvDestAlpha, + DestColor, + InvDestColor, + SrcAlphaSaturate, + BlendColor, + InvBlendColor, + SecondarySrcColor, + InvSecondarySrcColor, + SecondarySrcAlpha, + InvSecondarySrcAlpha, +}; + +namespace RenderTargetWriteMask +{ + typedef uint8_t Type; + enum + { + EnableNone = 0, + EnableRed = 0x01, + EnableGreen = 0x02, + EnableBlue = 0x04, + EnableAlpha = 0x08, + EnableAll = 0x0F, + }; +}; +typedef RenderTargetWriteMask::Type RenderTargetWriteMaskT; + +struct AspectBlendDesc +{ + BlendFactor srcFactor = BlendFactor::One; + BlendFactor dstFactor = BlendFactor::Zero; + BlendOp op = BlendOp::Add; +}; + +struct TargetBlendDesc +{ + AspectBlendDesc color; + AspectBlendDesc alpha; + + LogicOp logicOp = LogicOp::NoOp; + RenderTargetWriteMaskT writeMask = RenderTargetWriteMask::EnableAll; +}; + +struct BlendDesc +{ + TargetBlendDesc const* targets = nullptr; + UInt targetCount = 0; + + bool alphaToCoverateEnable = false; +}; + struct GraphicsPipelineStateDesc { ShaderProgram* program; @@ -657,6 +732,7 @@ struct GraphicsPipelineStateDesc UInt renderTargetCount; DepthStencilDesc depthStencil; RasterizerDesc rasterizer; + BlendDesc blend; }; struct ComputePipelineStateDesc @@ -670,6 +746,24 @@ class PipelineState : public Slang::RefObject public: }; +struct ScissorRect +{ + Int minX; + Int minY; + Int maxX; + Int maxY; +}; + +struct Viewport +{ + float originX = 0.0f; + float originY = 0.0f; + float extentX = 0.0f; + float extentY = 0.0f; + float minZ = 0.0f; + float maxZ = 1.0f; +}; + class Renderer: public Slang::RefObject { public: @@ -823,6 +917,18 @@ public: virtual void setDepthStencilTarget(ResourceView* depthStencilView) = 0; + virtual void setViewports(UInt count, Viewport const* viewports) = 0; + inline void setViewport(Viewport const& viewport) + { + setViewports(1, &viewport); + } + + virtual void setScissorRects(UInt count, ScissorRect const* rects) = 0; + inline void setScissorRect(ScissorRect const& rect) + { + setScissorRects(1, &rect); + } + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) = 0; virtual void draw(UInt vertexCount, UInt startVertex = 0) = 0; diff --git a/tools/gfx/window.cpp b/tools/gfx/window.cpp index ee9f50813..9bc63b896 100644 --- a/tools/gfx/window.cpp +++ b/tools/gfx/window.cpp @@ -111,22 +111,53 @@ int runWindowsApplication( struct Window { HWND handle; + WNDPROC nativeHook; }; +void setNativeWindowHook(Window* window, WNDPROC proc) +{ + window->nativeHook = proc; +} + static LRESULT CALLBACK windowProc( HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam) { + Window* window = (Window*) GetWindowLongPtrW(windowHandle, GWLP_USERDATA); + + // Give the installed filter a chance to intercept messages. + // (This is used for ImGui) + if( window ) + { + if(auto nativeHook = window->nativeHook) + { + auto result = nativeHook(windowHandle, message, wParam, lParam); + if(result) + return result; + } + } + // TODO: Actually implement some reasonable logic here. switch (message) { + case WM_CREATE: + { + auto createInfo = (CREATESTRUCTW*) lParam; + window = (Window*) createInfo->lpCreateParams; + window->handle = windowHandle; + + SetWindowLongPtrW(windowHandle, GWLP_USERDATA, (LONG)window); + } + break; + case WM_CLOSE: PostQuitMessage(0); return 0; } + return DefWindowProcW(windowHandle, message, wParam, lParam); } @@ -159,6 +190,8 @@ static ATOM getWindowClassAtom() Window* createWindow(WindowDesc const& desc) { Window* window = new Window(); + window->handle = nullptr; + window->nativeHook = nullptr; OSString windowTitle(desc.title); |
