diff --git a/.gitignore b/.gitignore index b84f7c1..dcc08c7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ *.pdb build/ .vscode/ +.idea +cmake-build-debug* diff --git a/CMakeLists.txt b/CMakeLists.txt index 885f8f4..6d7159d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,11 @@ project(rt64) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_VISIBILITY_PRESET hidden) +option(RT64_BUILD_EXAMPLES "Build examples for RT64" OFF) +if (${RT64_BUILD_EXAMPLES}) + set(RT64_STATIC ON) +endif() + function(preprocess INFILE OUTFILE OPTIONS) if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") @@ -29,7 +34,21 @@ if (WIN32) configure_file("${PROJECT_SOURCE_DIR}/src/contrib/dxc/bin/x64/dxcompiler.dll" "dxcompiler.dll" COPYONLY) configure_file("${PROJECT_SOURCE_DIR}/src/contrib/dxc/bin/x64/dxil.dll" "dxil.dll" COPYONLY) else() - set (DXC "LD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/src/contrib/dxc/lib/x64" "${PROJECT_SOURCE_DIR}/src/contrib/dxc/bin/x64/dxc") + if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + if (APPLE) + set (DXC "DYLD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/src/contrib/dxc/lib/x64" "${PROJECT_SOURCE_DIR}/src/contrib/dxc/bin/x64/dxc") + else() + set (DXC "LD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/src/contrib/dxc/lib/x64" "${PROJECT_SOURCE_DIR}/src/contrib/dxc/bin/x64/dxc") + endif() + else() + if (APPLE) + set (DXC "DYLD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/src/contrib/dxc/lib/arm64" "${PROJECT_SOURCE_DIR}/src/contrib/dxc/bin/arm64/dxc-macos") + else() + set (DXC "LD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/src/contrib/dxc/lib/arm64" "${PROJECT_SOURCE_DIR}/src/contrib/dxc/bin/arm64/dxc-linux") + endif() + endif() + + message(STATUS "DXC: ${DXC}") endif() # Build the file_to_c utility for converting binary files into a .c and .h file pair @@ -92,7 +111,7 @@ function(build_shader TARGETOBJ SHADERNAME OPTIONS) cmake_path(GET OUTNAME PARENT_PATH OUTPUT_DIR) file(MAKE_DIRECTORY ${OUTPUT_DIR}) # Compile DXIL shader binaries if building on Windows - if (${WIN32}) + if (WIN32) build_shader_dxil_impl(${TARGETOBJ} ${FILENAME} ${TARGET_NAME} ${OUTNAME} ${OPTIONS} ${EXTRA_ARGS}) endif() build_shader_spirv_impl(${TARGETOBJ} ${FILENAME} ${TARGET_NAME} ${OUTNAME} ${OPTIONS} ${EXTRA_ARGS}) @@ -155,13 +174,11 @@ function(build_geo_shader TARGETOBJ SHADERNAME) endfunction() function(build_ray_shader TARGETOBJ SHADERNAME) - if (NOT APPLE) - build_shader(${TARGETOBJ} ${SHADERNAME} "${DXC_RT_OPTS}" ${ARGN}) - endif() + build_shader(${TARGETOBJ} ${SHADERNAME} "${DXC_RT_OPTS}" ${ARGN}) endfunction() # Point cmake at src/contrib/mupen64plus-win32-deps/SDL2-2.26.3 for SDL2 on windows, look for an installed package on other systems -if (${WIN32}) +if (WIN32) set(SDL2_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/src/contrib/mupen64plus-win32-deps/SDL2-2.26.3/include") set(SDL2_LIBRARIES "SDL2" "SDL2main") link_directories("${PROJECT_SOURCE_DIR}/src/contrib/mupen64plus-win32-deps/SDL2-2.26.3/lib/x64") @@ -286,13 +303,17 @@ include_directories( "${PROJECT_SOURCE_DIR}/src/contrib/D3D12MemoryAllocator/src" "${PROJECT_SOURCE_DIR}/src/contrib/imgui" "${PROJECT_SOURCE_DIR}/src/contrib/hlslpp/include" - "${PROJECT_SOURCE_DIR}/src/contrib/dxc/inc" "${PROJECT_SOURCE_DIR}/src/contrib/Vulkan-Headers/include" "${PROJECT_SOURCE_DIR}/src/contrib/VulkanMemoryAllocator/include" "${PROJECT_SOURCE_DIR}/src/contrib/mupen64plus-core/src/api" "${PROJECT_SOURCE_DIR}/src/contrib/nativefiledialog-extended/src/include" ) +if (WIN32) + # DXC only needs to be included during runtime for Windows. + include_directories("${PROJECT_SOURCE_DIR}/src/contrib/dxc/inc") +endif() + option(RT64_STATIC "Build RT64 as a static library" OFF) if (${RT64_STATIC}) add_library(rt64 STATIC ${SOURCES}) @@ -307,7 +328,7 @@ set_target_properties(rt64 PROPERTIES PREFIX "") target_link_libraries(rt64 nfd) # Add any Windows-specific source files and libraries -if (${WIN32}) +if (WIN32) target_sources(rt64 PRIVATE "${PROJECT_SOURCE_DIR}/src/d3d12/rt64_d3d12.cpp" "${PROJECT_SOURCE_DIR}/src/contrib/imgui/backends/imgui_impl_dx12.cpp" @@ -327,7 +348,7 @@ if (NOT ANDROID) target_link_libraries(rt64 ${SDL2_LIBRARIES}) endif() -if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") +if (CMAKE_SYSTEM_NAME MATCHES "Linux") find_package(X11 REQUIRED) target_include_directories(rt64 PUBLIC ${X11_INCLUDE_DIR}) target_link_libraries(rt64 ${X11_LIBRARIES}) @@ -388,3 +409,17 @@ build_pixel_shader( rt64 "src/shaders/Im3DPS.hlsl") build_pixel_shader( rt64 "src/shaders/PostProcessPS.hlsl") target_include_directories(rt64 PRIVATE ${CMAKE_BINARY_DIR}/src) + +if (RT64_BUILD_EXAMPLES) + add_executable(rhi_test "examples/rt64_render_interface.cpp" "examples/rhi_test.cpp") + target_link_libraries(rhi_test rt64) + + build_pixel_shader( rhi_test "examples/shaders/RenderInterfaceTestPS.hlsl") + build_vertex_shader( rhi_test "examples/shaders/RenderInterfaceTestVS.hlsl") + build_compute_shader(rhi_test "examples/shaders/RenderInterfaceTestCS.hlsl") + build_ray_shader( rhi_test "examples/shaders/RenderInterfaceTestRT.hlsl") + build_pixel_shader( rhi_test "examples/shaders/RenderInterfaceTestPostPS.hlsl") + build_vertex_shader( rhi_test "examples/shaders/RenderInterfaceTestPostVS.hlsl") + + target_include_directories(rhi_test PRIVATE ${CMAKE_BINARY_DIR}/examples) +endif() diff --git a/src/rhi/test_main.cpp b/examples/rhi_test.cpp similarity index 61% rename from src/rhi/test_main.cpp rename to examples/rhi_test.cpp index 4990e4a..3a8ea59 100644 --- a/src/rhi/test_main.cpp +++ b/examples/rhi_test.cpp @@ -1,4 +1,3 @@ -#include #include "rhi/rt64_render_interface.h" namespace RT64 { @@ -6,21 +5,16 @@ namespace RT64 { extern std::unique_ptr CreateVulkanInterface(); } -int main() { - std::unique_ptr renderInterface; +int main(int argc, char** argv) { + std::unique_ptr renderInterface = RT64::CreateVulkanInterface(); - // Create a render interface with the preferred backend. +#ifdef _WIN32 + // Windows only: Can also use D3D12. const bool useVulkan = true; - if (useVulkan) { - renderInterface = RT64::CreateVulkanInterface(); - } - else { -#ifdef _WIN64 + if (!useVulkan) { renderInterface = RT64::CreateD3D12Interface(); -#else - assert(false); -#endif } +#endif // Execute a blocking test that creates a window and draws some geometry to test the render interface. RT64::RenderInterfaceTest(renderInterface.get()); diff --git a/examples/rt64_render_interface.cpp b/examples/rt64_render_interface.cpp new file mode 100644 index 0000000..fdb6701 --- /dev/null +++ b/examples/rt64_render_interface.cpp @@ -0,0 +1,796 @@ +// +// RT64 +// + +#include "rhi/rt64_render_interface.h" + +#include +#include +#include +#include + +#ifdef _WIN64 +#include "shaders/RenderInterfaceTestPS.hlsl.dxil.h" +#include "shaders/RenderInterfaceTestRT.hlsl.dxil.h" +#include "shaders/RenderInterfaceTestVS.hlsl.dxil.h" +#include "shaders/RenderInterfaceTestCS.hlsl.dxil.h" +#include "shaders/RenderInterfaceTestPostPS.hlsl.dxil.h" +#include "shaders/RenderInterfaceTestPostVS.hlsl.dxil.h" +#endif +#include "shaders/RenderInterfaceTestPS.hlsl.spirv.h" +#include "shaders/RenderInterfaceTestRT.hlsl.spirv.h" +#include "shaders/RenderInterfaceTestVS.hlsl.spirv.h" +#include "shaders/RenderInterfaceTestCS.hlsl.spirv.h" +#include "shaders/RenderInterfaceTestPostPS.hlsl.spirv.h" +#include "shaders/RenderInterfaceTestPostVS.hlsl.spirv.h" + +#define ENABLE_SWAP_CHAIN 1 +#define ENABLE_CLEAR 1 +#define ENABLE_RASTER 1 +#define ENABLE_TEXTURE 0 +#define ENABLE_COMPUTE 0 +#define ENABLE_RT 0 + +namespace RT64 { + struct RasterDescriptorSet : RenderDescriptorSetBase { + uint32_t gSampler; + uint32_t gTextures; + + std::unique_ptr linearSampler; + + RasterDescriptorSet(RenderDevice *device, uint32_t textureArraySize) { + linearSampler = device->createSampler(RenderSamplerDesc()); + + const uint32_t TextureArrayUpperRange = 512; + builder.begin(); + gSampler = builder.addImmutableSampler(1, linearSampler.get()); + gTextures = builder.addTexture(2, TextureArrayUpperRange); + builder.end(true, textureArraySize); + + create(device); + } + }; + + struct ComputeDescriptorFirstSet : RenderDescriptorSetBase { + uint32_t gBlueNoiseTexture; + uint32_t gSampler; + uint32_t gTarget; + + std::unique_ptr linearSampler; + + ComputeDescriptorFirstSet(RenderDevice *device) { + linearSampler = device->createSampler(RenderSamplerDesc()); + + builder.begin(); + gBlueNoiseTexture = builder.addTexture(1); + gSampler = builder.addImmutableSampler(2, linearSampler.get()); + builder.end(); + + create(device); + } + }; + + struct ComputeDescriptorSecondSet : RenderDescriptorSetBase { + uint32_t gTarget; + + ComputeDescriptorSecondSet(RenderDevice *device) { + builder.begin(); + gTarget = builder.addReadWriteTexture(16); + builder.end(); + + create(device); + } + }; + + struct RaytracingDescriptorSet : RenderDescriptorSetBase { + uint32_t gBVH; + uint32_t gOutput; + uint32_t gBufferParams; + + RaytracingDescriptorSet(RenderDevice *device) { + builder.begin(); + gBVH = builder.addAccelerationStructure(0); + gOutput = builder.addReadWriteTexture(1); + gBufferParams = builder.addStructuredBuffer(2); + builder.end(); + + create(device); + } + }; + + struct TestMembers { + static const uint32_t BufferCount = 2; + static const uint32_t MSAACount = 4; + static const RenderFormat ColorFormat = RenderFormat::R8G8B8A8_UNORM; + static const RenderFormat DepthFormat = RenderFormat::D32_FLOAT; +#ifdef __ANDROID__ + static const RenderFormat SwapchainFormat = RenderFormat::R8G8B8A8_UNORM; +#else + static const RenderFormat SwapchainFormat = RenderFormat::B8G8R8A8_UNORM; +#endif + const RenderInterface *renderInterface = nullptr; + std::unique_ptr device; + std::unique_ptr commandQueue; + std::unique_ptr commandList; + std::unique_ptr commandFence; + std::unique_ptr swapChain; + std::unique_ptr framebuffer; + std::vector> swapFramebuffers; + std::unique_ptr linearSampler; + std::unique_ptr postSampler; + std::unique_ptr rasterSet; + std::unique_ptr computeFirstSet; + std::unique_ptr computeSecondSet; + std::unique_ptr rtSet; + std::unique_ptr postSet; + std::unique_ptr rasterPipelineLayout; + std::unique_ptr computePipelineLayout; + std::unique_ptr rtPipelineLayout; + std::unique_ptr postPipelineLayout; + std::unique_ptr rasterPipeline; + std::unique_ptr computePipeline; + std::unique_ptr rtPipeline; + std::unique_ptr postPipeline; + std::unique_ptr colorTargetMS; + std::unique_ptr colorTargetResolved; + std::unique_ptr depthTarget; + std::unique_ptr uploadBuffer; + std::unique_ptr blueNoiseTexture; + std::unique_ptr vertexBuffer; + std::unique_ptr indexBuffer; + std::unique_ptr rtParamsBuffer; + std::unique_ptr rtVertexBuffer; + std::unique_ptr rtScratchBuffer; + std::unique_ptr rtInstancesBuffer; + std::unique_ptr rtBottomLevelASBuffer; + std::unique_ptr rtBottomLevelAS; + std::unique_ptr rtTopLevelASBuffer; + std::unique_ptr rtTopLevelAS; + std::unique_ptr rtShaderBindingTableBuffer; + RenderShaderBindingTableInfo rtShaderBindingTableInfo; + RenderVertexBufferView vertexBufferView; + RenderIndexBufferView indexBufferView; + RenderInputSlot inputSlot; + } Test; + + struct RasterPushConstant { + float colorAdd[4] = {}; + uint32_t textureIndex = 0; + }; + + struct ComputePushConstant { + float Multiply[4] = {}; + uint32_t Resolution[2] = {}; + }; + + void TestInitialize(RenderInterface *renderInterface, RenderWindow window) { + Test.renderInterface = renderInterface; + Test.device = renderInterface->createDevice(); + Test.commandQueue = Test.device->createCommandQueue(RenderCommandListType::DIRECT); + Test.commandList = Test.device->createCommandList(RenderCommandListType::DIRECT); + Test.commandFence = Test.device->createCommandFence(); + +# if ENABLE_SWAP_CHAIN + Test.swapChain = Test.commandQueue->createSwapChain(window, Test.BufferCount, Test.SwapchainFormat); +# endif + +# if ENABLE_RASTER + const uint32_t textureArraySize = 3; + Test.rasterSet = std::make_unique(Test.device.get(), 3); + + RenderPipelineLayoutBuilder layoutBuilder; + layoutBuilder.begin(false, true); + layoutBuilder.addPushConstant(0, 0, sizeof(RasterPushConstant), RenderShaderStageFlag::PIXEL); + layoutBuilder.addDescriptorSet(Test.rasterSet->builder); + layoutBuilder.end(); + + Test.rasterPipelineLayout = layoutBuilder.create(Test.device.get()); + + // Pick shader format depending on the render interface's requirements. + const RenderInterfaceCapabilities &interfaceCapabilities = Test.renderInterface->getCapabilities(); + const RenderShaderFormat shaderFormat = interfaceCapabilities.shaderFormat; + const void *PSBlob = nullptr; + const void *VSBlob = nullptr; + const void *CSBlob = nullptr; + const void *RTBlob = nullptr; + const void *PostPSBlob = nullptr; + const void *PostVSBlob = nullptr; + uint64_t PSBlobSize = 0; + uint64_t VSBlobSize = 0; + uint64_t CSBlobSize = 0; + uint64_t RTBlobSize = 0; + uint64_t PostPSBlobSize = 0; + uint64_t PostVSBlobSize = 0; + switch (shaderFormat) { +#ifdef _WIN64 + case RenderShaderFormat::DXIL: + PSBlob = RenderInterfaceTestPSBlobDXIL; + PSBlobSize = sizeof(RenderInterfaceTestPSBlobDXIL); + VSBlob = RenderInterfaceTestVSBlobDXIL; + VSBlobSize = sizeof(RenderInterfaceTestVSBlobDXIL); + CSBlob = RenderInterfaceTestCSBlobDXIL; + CSBlobSize = sizeof(RenderInterfaceTestCSBlobDXIL); + RTBlob = RenderInterfaceTestRTBlobDXIL; + RTBlobSize = sizeof(RenderInterfaceTestRTBlobDXIL); + PostPSBlob = RenderInterfaceTestPostPSBlobDXIL; + PostPSBlobSize = sizeof(RenderInterfaceTestPostPSBlobDXIL); + PostVSBlob = RenderInterfaceTestPostVSBlobDXIL; + PostVSBlobSize = sizeof(RenderInterfaceTestPostVSBlobDXIL); + break; +#endif + case RenderShaderFormat::SPIRV: + PSBlob = RenderInterfaceTestPSBlobSPIRV; + PSBlobSize = sizeof(RenderInterfaceTestPSBlobSPIRV); + VSBlob = RenderInterfaceTestVSBlobSPIRV; + VSBlobSize = sizeof(RenderInterfaceTestVSBlobSPIRV); + CSBlob = RenderInterfaceTestCSBlobSPIRV; + CSBlobSize = sizeof(RenderInterfaceTestCSBlobSPIRV); + RTBlob = RenderInterfaceTestRTBlobSPIRV; + RTBlobSize = sizeof(RenderInterfaceTestRTBlobSPIRV); + PostPSBlob = RenderInterfaceTestPostPSBlobSPIRV; + PostPSBlobSize = sizeof(RenderInterfaceTestPostPSBlobSPIRV); + PostVSBlob = RenderInterfaceTestPostVSBlobSPIRV; + PostVSBlobSize = sizeof(RenderInterfaceTestPostVSBlobSPIRV); + break; + default: + assert(false && "Unknown shader format."); + break; + } + + const uint32_t VertexCount = 3; + const uint32_t FloatsPerVertex = 4; + const float Vertices[VertexCount * FloatsPerVertex] = { + -0.5f, -0.25f, 0.0f, 0.0f, + 0.5f, -0.25f, 1.0f, 0.0f, + 0.25f, 0.25f, 0.0f, 1.0f + }; + + const uint32_t Indices[3] = { + 0, 1, 2 + }; + + Test.inputSlot = RenderInputSlot(0, sizeof(float) * FloatsPerVertex); + + std::vector inputElements; + inputElements.emplace_back(RenderInputElement("POSITION", 0, 0, RenderFormat::R32G32_FLOAT, 0, 0)); + inputElements.emplace_back(RenderInputElement("TEXCOORD", 0, 1, RenderFormat::R32G32_FLOAT, 0, sizeof(float) * 2)); + + std::unique_ptr pixelShader = Test.device->createShader(PSBlob, PSBlobSize, "PSMain", shaderFormat); + std::unique_ptr vertexShader = Test.device->createShader(VSBlob, VSBlobSize, "VSMain", shaderFormat); + + RenderGraphicsPipelineDesc graphicsDesc; + graphicsDesc.inputSlots = &Test.inputSlot; + graphicsDesc.inputSlotsCount = 1; + graphicsDesc.inputElements = inputElements.data(); + graphicsDesc.inputElementsCount = uint32_t(inputElements.size()); + graphicsDesc.pipelineLayout = Test.rasterPipelineLayout.get(); + graphicsDesc.pixelShader = pixelShader.get(); + graphicsDesc.vertexShader = vertexShader.get(); + graphicsDesc.renderTargetFormat[0] = Test.ColorFormat; + graphicsDesc.renderTargetBlend[0] = RenderBlendDesc::Copy(); + graphicsDesc.depthTargetFormat = Test.DepthFormat; + graphicsDesc.renderTargetCount = 1; + graphicsDesc.multisampling.sampleCount = Test.MSAACount; + Test.rasterPipeline = Test.device->createGraphicsPipeline(graphicsDesc); + + Test.postSampler = Test.device->createSampler(RenderSamplerDesc()); + const RenderSampler *postSamplerPtr = Test.postSampler.get(); + + // Create the post processing pipeline + std::vector postDescriptorRanges = { + RenderDescriptorRange(RenderDescriptorRangeType::TEXTURE, 1, 1), + RenderDescriptorRange(RenderDescriptorRangeType::SAMPLER, 2, 1, &postSamplerPtr) + }; + + RenderDescriptorSetDesc postDescriptorSetDesc(postDescriptorRanges.data(), uint32_t(postDescriptorRanges.size())); + Test.postSet = Test.device->createDescriptorSet(postDescriptorSetDesc); + Test.postPipelineLayout = Test.device->createPipelineLayout(RenderPipelineLayoutDesc(nullptr, 0, &postDescriptorSetDesc, 1, false, true)); + + inputElements.clear(); + inputElements.emplace_back(RenderInputElement("POSITION", 0, 0, RenderFormat::R32G32_FLOAT, 0, 0)); + + std::unique_ptr postPixelShader = Test.device->createShader(PostPSBlob, PostPSBlobSize, "PSMain", shaderFormat); + std::unique_ptr postVertexShader = Test.device->createShader(PostVSBlob, PostVSBlobSize, "VSMain", shaderFormat); + + RenderGraphicsPipelineDesc postDesc; + postDesc.inputSlots = nullptr; + postDesc.inputSlotsCount = 0; + postDesc.inputElements = nullptr; + postDesc.inputElementsCount = 0; + postDesc.pipelineLayout = Test.postPipelineLayout.get(); + postDesc.pixelShader = postPixelShader.get(); + postDesc.vertexShader = postVertexShader.get(); + postDesc.renderTargetFormat[0] = Test.SwapchainFormat; + postDesc.renderTargetBlend[0] = RenderBlendDesc::Copy(); + postDesc.renderTargetCount = 1; + Test.postPipeline = Test.device->createGraphicsPipeline(postDesc); + +# if ENABLE_TEXTURE + // Upload a texture. + const uint32_t Width = 512; + const uint32_t Height = 512; + const uint32_t RowLength = Width; + const RenderFormat Format = RenderFormat::R8G8B8A8_UNORM; + const uint32_t BufferSize = RowLength * Height * RenderFormatSize(Format); + Test.uploadBuffer = Test.device->createBuffer(RenderBufferDesc::UploadBuffer(BufferSize)); + Test.blueNoiseTexture = Test.device->createTexture(RenderTextureDesc::Texture2D(Width, Height, 1, Format)); + Test.rasterSet->setTexture(Test.rasterSet->gTextures + 2, Test.blueNoiseTexture.get(), RenderTextureLayout::SHADER_READ); + + // Copy to upload buffer. + void *bufferData = Test.uploadBuffer->map(); + memcpy(bufferData, LDR_64_64_64_RGB1_BGRA8, BufferSize); + Test.uploadBuffer->unmap(); + + // Run command list to copy the upload buffer to the texture. + Test.commandList->begin(); + Test.commandList->barriers(RenderBarrierStage::COPY, + RenderBufferBarrier(Test.uploadBuffer.get(), RenderBufferAccess::READ), + RenderTextureBarrier(Test.blueNoiseTexture.get(), RenderTextureLayout::COPY_DEST) + ); + + Test.commandList->copyTextureRegion( + RenderTextureCopyLocation::Subresource(Test.blueNoiseTexture.get()), + RenderTextureCopyLocation::PlacedFootprint(Test.uploadBuffer.get(), Format, Width, Height, 1, RowLength)); + + Test.commandList->barriers(RenderBarrierStage::GRAPHICS_AND_COMPUTE, RenderTextureBarrier(Test.blueNoiseTexture.get(), RenderTextureLayout::SHADER_READ)); + Test.commandList->end(); + Test.commandQueue->executeCommandLists(Test.commandList.get(), Test.commandFence.get()); + Test.commandQueue->waitForCommandFence(Test.commandFence.get()); +# endif + + Test.vertexBuffer = Test.device->createBuffer(RenderBufferDesc::VertexBuffer(sizeof(Vertices), RenderHeapType::UPLOAD)); + void *dstData = Test.vertexBuffer->map(); + memcpy(dstData, Vertices, sizeof(Vertices)); + Test.vertexBuffer->unmap(); + Test.vertexBufferView = RenderVertexBufferView(Test.vertexBuffer.get(), sizeof(Vertices)); + + Test.indexBuffer = Test.device->createBuffer(RenderBufferDesc::IndexBuffer(sizeof(Indices), RenderHeapType::UPLOAD)); + dstData = Test.indexBuffer->map(); + memcpy(dstData, Indices, sizeof(Indices)); + Test.indexBuffer->unmap(); + Test.indexBufferView = RenderIndexBufferView(Test.indexBuffer.get(), sizeof(Indices), RenderFormat::R32_UINT); +# endif + +# if ENABLE_COMPUTE + Test.computeFirstSet = std::make_unique(Test.device.get()); + Test.computeSecondSet = std::make_unique(Test.device.get()); + Test.computeFirstSet->setTexture(Test.computeFirstSet->gBlueNoiseTexture, Test.blueNoiseTexture.get(), RenderTextureLayout::SHADER_READ); + + layoutBuilder.begin(); + layoutBuilder.addPushConstant(0, 0, sizeof(ComputePushConstant), RenderShaderStageFlag::COMPUTE); + layoutBuilder.addDescriptorSet(Test.computeFirstSet->builder); + layoutBuilder.addDescriptorSet(Test.computeSecondSet->builder); + layoutBuilder.end(); + + Test.computePipelineLayout = layoutBuilder.create(Test.device.get()); + + std::unique_ptr computeShader = Test.device->createShader(CSBlob, CSBlobSize, "CSMain", shaderFormat); + RenderComputePipelineDesc computeDesc; + computeDesc.computeShader = computeShader.get(); + computeDesc.pipelineLayout = Test.computePipelineLayout.get(); + Test.computePipeline = Test.device->createComputePipeline(computeDesc); +# endif + +# if ENABLE_RT + Test.rtSet = std::make_unique(Test.device.get()); + + layoutBuilder.begin(true); + layoutBuilder.addDescriptorSet(Test.rtSet->builder); + layoutBuilder.end(); + + Test.rtPipelineLayout = layoutBuilder.create(Test.device.get()); + + struct BufferParams { + float rgbMultiplier[3]; + uint32_t pad[3]; + }; + + BufferParams paramsToUpload[2]; + paramsToUpload[0].rgbMultiplier[0] = 1.0f; + paramsToUpload[0].rgbMultiplier[1] = 0.25f; + paramsToUpload[0].rgbMultiplier[2] = 0.25f; + paramsToUpload[1].rgbMultiplier[0] = 0.25f; + paramsToUpload[1].rgbMultiplier[1] = 1.0f; + paramsToUpload[1].rgbMultiplier[2] = 0.25f; + + RenderBufferStructuredView paramsView(sizeof(BufferParams)); + Test.rtParamsBuffer = Test.device->createBuffer(RenderBufferDesc::UploadBuffer(sizeof(paramsToUpload), RenderBufferFlag::STORAGE)); + Test.rtSet->setBuffer(Test.rtSet->gBufferParams, Test.rtParamsBuffer.get(), sizeof(paramsToUpload), ¶msView); + dstData = Test.rtParamsBuffer->map(); + memcpy(dstData, paramsToUpload, sizeof(paramsToUpload)); + Test.rtParamsBuffer->unmap(); + + RenderRaytracingPipelineLibrarySymbol rtLibrarySymbols[] = { + RenderRaytracingPipelineLibrarySymbol("ColorRayGen", RenderRaytracingPipelineLibrarySymbolType::RAYGEN), + RenderRaytracingPipelineLibrarySymbol("ColorClosestHit", RenderRaytracingPipelineLibrarySymbolType::CLOSEST_HIT), + RenderRaytracingPipelineLibrarySymbol("ColorMiss", RenderRaytracingPipelineLibrarySymbolType::MISS) + }; + + std::unique_ptr rtShader = Test.device->createShader(RTBlob, RTBlobSize, nullptr, shaderFormat); + RenderRaytracingPipelineLibrary rtLibrary(rtShader.get(), rtLibrarySymbols, uint32_t(std::size(rtLibrarySymbols))); + RenderRaytracingPipelineHitGroup rtHitGroup("ColorHitGroup", "ColorClosestHit"); + RenderRaytracingPipelineDesc rtPsoDesc; + rtPsoDesc.libraries = &rtLibrary; + rtPsoDesc.librariesCount = 1; + rtPsoDesc.hitGroups = &rtHitGroup; + rtPsoDesc.hitGroupsCount = 1; + rtPsoDesc.pipelineLayout = Test.rtPipelineLayout.get(); + rtPsoDesc.maxPayloadSize = sizeof(float) * 4; + Test.rtPipeline = Test.device->createRaytracingPipeline(rtPsoDesc); + + // Create RT BVH. + const float BLASVertices[] = { + 0.25f, 0.25f, 1.0f, + 0.75f, 0.25f, 1.0f, + 0.5f, 0.75f, 1.0f + }; + + dstData = Test.uploadBuffer->map(); + memcpy(dstData, BLASVertices, sizeof(BLASVertices)); + Test.uploadBuffer->unmap(); + + Test.rtVertexBuffer = Test.device->createBuffer(RenderBufferDesc::VertexBuffer(sizeof(BLASVertices), RenderHeapType::DEFAULT, RenderBufferFlag::ACCELERATION_STRUCTURE_INPUT)); + + RenderBottomLevelASMesh blasMesh; + blasMesh.vertexBuffer = Test.rtVertexBuffer.get(); + blasMesh.vertexFormat = RenderFormat::R32G32B32_FLOAT; + blasMesh.vertexCount = 3; + blasMesh.vertexStride = sizeof(float) * 3; + blasMesh.isOpaque = true; + + RenderBottomLevelASBuildInfo blasBuildInfo; + Test.device->setBottomLevelASBuildInfo(blasBuildInfo, &blasMesh, 1); + + Test.rtBottomLevelASBuffer = Test.device->createBuffer(RenderBufferDesc::AccelerationStructureBuffer(blasBuildInfo.accelerationStructureSize)); + Test.rtBottomLevelAS = Test.device->createAccelerationStructure(RenderAccelerationStructureDesc(RenderAccelerationStructureType::BOTTOM_LEVEL, Test.rtBottomLevelASBuffer.get(), blasBuildInfo.accelerationStructureSize)); + + RenderTopLevelASInstance tlasInstance; + tlasInstance.bottomLevelAS = Test.rtBottomLevelASBuffer.get(); + tlasInstance.instanceMask = 0xFF; + tlasInstance.cullDisable = true; + + RenderTopLevelASBuildInfo tlasBuildInfo; + Test.device->setTopLevelASBuildInfo(tlasBuildInfo, &tlasInstance, 1); + + Test.rtInstancesBuffer = Test.device->createBuffer(RenderBufferDesc::UploadBuffer(tlasBuildInfo.instancesBufferData.size(), RenderBufferFlag::ACCELERATION_STRUCTURE_INPUT)); + dstData = Test.rtInstancesBuffer->map(); + memcpy(dstData, tlasBuildInfo.instancesBufferData.data(), tlasBuildInfo.instancesBufferData.size()); + Test.rtInstancesBuffer->unmap(); + + Test.rtScratchBuffer = Test.device->createBuffer(RenderBufferDesc::DefaultBuffer(std::max(blasBuildInfo.scratchSize, tlasBuildInfo.scratchSize), RenderBufferFlag::ACCELERATION_STRUCTURE_SCRATCH)); + Test.rtTopLevelASBuffer = Test.device->createBuffer(RenderBufferDesc::AccelerationStructureBuffer(tlasBuildInfo.accelerationStructureSize)); + Test.rtTopLevelAS = Test.device->createAccelerationStructure(RenderAccelerationStructureDesc(RenderAccelerationStructureType::TOP_LEVEL, Test.rtTopLevelASBuffer.get(), tlasBuildInfo.accelerationStructureSize)); + Test.rtSet->setAccelerationStructure(Test.rtSet->gBVH, Test.rtTopLevelAS.get()); + + RenderShaderBindingGroups bindingGroups; + RenderPipelineProgram rayGenProgram = Test.rtPipeline->getProgram("ColorRayGen"); + RenderPipelineProgram missProgram = Test.rtPipeline->getProgram("ColorMiss"); + RenderPipelineProgram hitGroupProgram = Test.rtPipeline->getProgram("ColorHitGroup"); + bindingGroups.rayGen = RenderShaderBindingGroup(&rayGenProgram, 1); + bindingGroups.miss = RenderShaderBindingGroup(&missProgram, 1); + bindingGroups.hitGroup = RenderShaderBindingGroup(&hitGroupProgram, 1); + + RenderDescriptorSet *rtSetPtr = Test.rtSet->get(); + Test.device->setShaderBindingTableInfo(Test.rtShaderBindingTableInfo, bindingGroups, Test.rtPipeline.get(), &rtSetPtr, 1); + + const std::vector tableData = Test.rtShaderBindingTableInfo.tableBufferData; + Test.rtShaderBindingTableBuffer = Test.device->createBuffer(RenderBufferDesc::UploadBuffer(tableData.size(), RenderBufferFlag::SHADER_BINDING_TABLE)); + dstData = Test.rtShaderBindingTableBuffer->map(); + memcpy(dstData, tableData.data(), tableData.size()); + Test.rtShaderBindingTableBuffer->unmap(); + + // Run command list to copy the vertex buffer and build the BLAS/TLAS. + Test.commandList->begin(); + Test.commandList->barriers(RenderBarrierStage::COPY, RenderBufferBarrier(Test.rtVertexBuffer.get(), RenderBufferAccess::WRITE)); + Test.commandList->copyBufferRegion(Test.rtVertexBuffer.get(), Test.uploadBuffer.get(), sizeof(BLASVertices)); + Test.commandList->barriers(RenderBarrierStage::COMPUTE, RenderBufferBarrier(Test.rtVertexBuffer.get(), RenderBufferAccess::READ)); + Test.commandList->buildBottomLevelAS(Test.rtBottomLevelAS.get(), Test.rtScratchBuffer.get(), blasBuildInfo); + Test.commandList->barriers(RenderBarrierStage::NONE, RenderBufferBarrier(Test.rtBottomLevelASBuffer.get(), RenderBufferAccess::READ)); + Test.commandList->buildTopLevelAS(Test.rtTopLevelAS.get(), Test.rtScratchBuffer.get(), Test.rtInstancesBuffer.get(), tlasBuildInfo); + Test.commandList->end(); + Test.commandQueue->executeCommandLists(Test.commandList.get(), Test.commandFence.get()); + Test.commandQueue->waitForCommandFence(Test.commandFence.get()); +# endif + } + + void TestResize() { + // Resize can be called during window creation by Windows. + if (Test.swapChain == nullptr) { + return; + } + +# if ENABLE_SWAP_CHAIN + Test.swapFramebuffers.clear(); + Test.swapChain->resize(); + Test.swapFramebuffers.resize(Test.swapChain->getTextureCount()); + for (uint32_t i = 0; i < Test.swapChain->getTextureCount(); i++) { + const RenderTexture *curTex = Test.swapChain->getTexture(i); + Test.swapFramebuffers[i] = Test.device->createFramebuffer(RenderFramebufferDesc{&curTex, 1}); + } +# endif +# if ENABLE_CLEAR + Test.colorTargetMS = Test.device->createTexture(RenderTextureDesc::ColorTarget(Test.swapChain->getWidth(), Test.swapChain->getHeight(), Test.ColorFormat, RenderMultisampling(Test.MSAACount), nullptr)); + Test.colorTargetResolved = Test.device->createTexture(RenderTextureDesc::ColorTarget(Test.swapChain->getWidth(), Test.swapChain->getHeight(), Test.ColorFormat, 1, nullptr, RenderTextureFlag::STORAGE | RenderTextureFlag::UNORDERED_ACCESS)); + Test.depthTarget = Test.device->createTexture(RenderTextureDesc::DepthTarget(Test.swapChain->getWidth(), Test.swapChain->getHeight(), Test.DepthFormat, RenderMultisampling(Test.MSAACount))); + + const RenderTexture *colorTargetPtr = Test.colorTargetMS.get(); + Test.framebuffer = Test.device->createFramebuffer(RenderFramebufferDesc(&colorTargetPtr, 1, Test.depthTarget.get())); +# endif +# if ENABLE_COMPUTE + Test.computeSecondSet->setTexture(Test.computeSecondSet->gTarget, Test.colorTarget.get(), RenderTextureLayout::GENERAL); +# endif +# if ENABLE_RT + Test.rtSet->setTexture(Test.rtSet->gOutput, Test.colorTarget.get(), RenderTextureLayout::GENERAL); +# endif + } + + void TestDraw() { + // Begin. + Test.commandList->begin(); +# if ENABLE_CLEAR + const uint32_t SyncInterval = 1; + const uint32_t width = Test.swapChain->getWidth(); + const uint32_t height = Test.swapChain->getHeight(); + const RenderViewport viewport(0.0f, 0.0f, float(width), float(height)); + const RenderRect scissor(0, 0, width, height); + + // Clear. + Test.commandList->setViewports(viewport); + Test.commandList->setScissors(scissor); + Test.commandList->barriers(RenderBarrierStage::GRAPHICS, RenderTextureBarrier(Test.colorTargetMS.get(), RenderTextureLayout::COLOR_WRITE)); + Test.commandList->barriers(RenderBarrierStage::GRAPHICS, RenderTextureBarrier(Test.depthTarget.get(), RenderTextureLayout::DEPTH_WRITE)); + Test.commandList->setFramebuffer(Test.framebuffer.get()); + Test.commandList->clearColor(0, RenderColor(0.0f, 0.0f, 0.5f)); + Test.commandList->clearDepth(); +# endif +# if ENABLE_RASTER + // Raster. + RasterPushConstant pushConstant; + pushConstant.colorAdd[0] = 0.5f; + pushConstant.colorAdd[1] = 0.25f; + pushConstant.colorAdd[2] = 0.0f; + pushConstant.colorAdd[3] = 0.0f; + pushConstant.textureIndex = 2; + Test.commandList->setPipeline(Test.rasterPipeline.get()); + Test.commandList->setGraphicsPipelineLayout(Test.rasterPipelineLayout.get()); + Test.commandList->setGraphicsPushConstants(0, &pushConstant); +# if ENABLE_TEXTURE + Test.commandList->setGraphicsDescriptorSet(Test.rasterSet->get(), 0); +# endif + Test.commandList->setVertexBuffers(0, &Test.vertexBufferView, 1, &Test.inputSlot); + Test.commandList->setIndexBuffer(&Test.indexBufferView); + Test.commandList->drawInstanced(3, 1, 0, 0); + Test.commandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(Test.depthTarget.get(), RenderTextureLayout::DEPTH_READ)); +# endif +# if ENABLE_COMPUTE || ENABLE_RT || ENABLE_CLEAR + Test.commandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(Test.colorTargetMS.get(), RenderTextureLayout::RESOLVE_SOURCE)); + Test.commandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(Test.colorTargetResolved.get(), RenderTextureLayout::RESOLVE_DEST)); + Test.commandList->resolveTexture(Test.colorTargetResolved.get(), Test.colorTargetMS.get()); +# endif +# if ENABLE_COMPUTE || ENABLE_RT + Test.commandList->barriers(RenderBarrierStage::COMPUTE, RenderTextureBarrier(Test.colorTargetResolved.get(), RenderTextureLayout::GENERAL)); +# endif +# if ENABLE_COMPUTE + const uint32_t GroupCount = 8; + ComputePushConstant pushCostant; + pushCostant.Resolution[0] = width; + pushCostant.Resolution[1] = height; + pushCostant.Multiply[0] = 0.5f; + pushCostant.Multiply[1] = 0.5f; + pushCostant.Multiply[2] = 1.0f; + pushCostant.Multiply[3] = 1.0f; + Test.commandList->setPipeline(Test.computePipeline.get()); + Test.commandList->setComputePipelineLayout(Test.computePipelineLayout.get()); + Test.commandList->setComputePushConstants(0, &pushCostant); + Test.commandList->setComputeDescriptorSet(Test.computeFirstSet->get(), 0); + Test.commandList->setComputeDescriptorSet(Test.computeSecondSet->get(), 1); + Test.commandList->dispatch((width + GroupCount - 1) / GroupCount, (height + GroupCount - 1) / GroupCount, 1); +# endif +# if ENABLE_RT + // RT. + Test.commandList->barriers(RenderBarrierStage::COMPUTE, RenderTextureBarrier(Test.colorTargetResolved.get(), RenderTextureLayout::GENERAL)); + Test.commandList->setPipeline(Test.rtPipeline.get()); + Test.commandList->setRaytracingPipelineLayout(Test.rtPipelineLayout.get()); + Test.commandList->setRaytracingDescriptorSet(Test.rtSet->get(), 0); + Test.commandList->traceRays(width, height, 1, Test.rtShaderBindingTableBuffer.get(), Test.rtShaderBindingTableInfo.groups); +# endif +# if ENABLE_SWAP_CHAIN + const uint32_t swapChainTextureIndex = Test.swapChain->getTextureIndex(); + RenderTexture *swapChainTexture = Test.swapChain->getTexture(swapChainTextureIndex); + RenderFramebuffer *swapFramebuffer = Test.swapFramebuffers[swapChainTextureIndex].get(); + Test.commandList->setViewports(viewport); + Test.commandList->setScissors(scissor); + Test.commandList->barriers(RenderBarrierStage::GRAPHICS, RenderTextureBarrier(swapChainTexture, RenderTextureLayout::COLOR_WRITE)); + Test.commandList->setFramebuffer(swapFramebuffer); +# endif +# if ENABLE_CLEAR + Test.commandList->barriers(RenderBarrierStage::GRAPHICS, RenderTextureBarrier(Test.colorTargetResolved.get(), RenderTextureLayout::SHADER_READ)); + Test.commandList->clearColor(0, RenderColor(0.0f, 0.0f, 0.0f)); + Test.commandList->setPipeline(Test.postPipeline.get()); + Test.commandList->setGraphicsPipelineLayout(Test.postPipelineLayout.get()); + Test.postSet->setTexture(0, Test.colorTargetResolved.get(), RenderTextureLayout::SHADER_READ); + Test.commandList->setGraphicsDescriptorSet(Test.postSet.get(), 0); + Test.commandList->drawInstanced(3, 1, 0, 0); +# endif +# if ENABLE_SWAP_CHAIN + Test.commandList->barriers(RenderBarrierStage::NONE, RenderTextureBarrier(swapChainTexture, RenderTextureLayout::PRESENT)); +# endif + // End. + Test.commandList->end(); + Test.commandQueue->executeCommandLists(Test.commandList.get(), Test.commandFence.get()); +# if ENABLE_SWAP_CHAIN + Test.swapChain->present(); +# endif + Test.commandQueue->waitForCommandFence(Test.commandFence.get()); + } + + void TestShutdown() { + Test.rtParamsBuffer.reset(nullptr); + Test.rtVertexBuffer.reset(nullptr); + Test.rtScratchBuffer.reset(nullptr); + Test.rtInstancesBuffer.reset(nullptr); + Test.rtBottomLevelASBuffer.reset(nullptr); + Test.rtTopLevelASBuffer.reset(nullptr); + Test.rtShaderBindingTableBuffer.reset(nullptr); + Test.uploadBuffer.reset(nullptr); + Test.blueNoiseTexture.reset(nullptr); + Test.vertexBuffer.reset(nullptr); + Test.indexBuffer.reset(nullptr); + Test.rasterPipeline.reset(nullptr); + Test.computePipeline.reset(nullptr); + Test.rtPipeline.reset(nullptr); + Test.postPipeline.reset(nullptr); + Test.rasterPipelineLayout.reset(nullptr); + Test.computePipelineLayout.reset(nullptr); + Test.rtPipelineLayout.reset(nullptr); + Test.postPipelineLayout.reset(nullptr); + Test.rtSet.reset(nullptr); + Test.rasterSet.reset(nullptr); + Test.computeFirstSet.reset(nullptr); + Test.computeSecondSet.reset(nullptr); + Test.postSet.reset(nullptr); + Test.linearSampler.reset(nullptr); + Test.postSampler.reset(nullptr); + Test.colorTargetMS.reset(nullptr); + Test.colorTargetResolved.reset(nullptr); + Test.framebuffer.reset(nullptr); + Test.swapFramebuffers.clear(); + Test.commandList.reset(nullptr); + Test.commandFence.reset(nullptr); + Test.swapChain.reset(nullptr); + Test.commandQueue.reset(nullptr); + Test.device.reset(nullptr); + } + +#if defined(_WIN64) + static LRESULT CALLBACK TestWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_CLOSE: + PostQuitMessage(0); + break; + case WM_SIZE: + TestResize(); + return 0; + case WM_PAINT: + TestDraw(); + return 0; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return 0; + } + + static HWND TestCreateWindow() { + // Register window class. + WNDCLASS wc; + memset(&wc, 0, sizeof(WNDCLASS)); + wc.lpfnWndProc = TestWndProc; + wc.hInstance = GetModuleHandle(0); + wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); + wc.lpszClassName = "RenderInterfaceTest"; + RegisterClass(&wc); + + // Create window. + const int Width = 1280; + const int Height = 720; + RECT rect; + UINT dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE; + rect.left = (GetSystemMetrics(SM_CXSCREEN) - Width) / 2; + rect.top = (GetSystemMetrics(SM_CYSCREEN) - Height) / 2; + rect.right = rect.left + Width; + rect.bottom = rect.top + Height; + AdjustWindowRectEx(&rect, dwStyle, 0, 0); + + return CreateWindow(wc.lpszClassName, "Render Interface Test", dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0, 0, wc.hInstance, NULL); + } + + void RenderInterfaceTest(RenderInterface *renderInterface) { + HWND hwnd = TestCreateWindow(); + TestInitialize(renderInterface, hwnd); + TestResize(); + + // Message loop. + MSG msg = {}; + while (msg.message != WM_QUIT) { + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + TestShutdown(); + DestroyWindow(hwnd); + } +#elif defined(__ANDROID__) + void RenderInterfaceTest(RenderInterface* renderInterface) { + assert(false); + } +#elif defined(__linux__) + void RenderInterfaceTest(RenderInterface* renderInterface) { + Display* display = XOpenDisplay(nullptr); + int blackColor = BlackPixel(display, DefaultScreen(display)); + Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), + 0, 0, 1280, 720, 0, blackColor, blackColor); + + + XSelectInput(display, window, StructureNotifyMask); + // Map the window and wait for the notify event to come in. + XMapWindow(display, window); + while (true) { + XEvent event; + XNextEvent(display, &event); + if (event.type == MapNotify) { + break; + } + } + + // Set up the delete window protocol. + Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, window, &wmDeleteMessage, 1); + + TestInitialize(renderInterface, {display, window}); + TestResize(); + TestDraw(); + + // Loop until the window is closed. + std::chrono::system_clock::time_point prev_frame = std::chrono::system_clock::now(); + bool running = true; + while (running) { + if (XPending(display) > 0) { + XEvent event; + XNextEvent(display, &event); + + switch (event.type) { + case Expose: + TestDraw(); + break; + + case ClientMessage: + if (event.xclient.data.l[0] == wmDeleteMessage) + running = false; + break; + + default: + break; + } + } + + using namespace std::chrono_literals; + std::this_thread::sleep_for(1ms); + auto now_time = std::chrono::system_clock::now(); + if (now_time - prev_frame > 16666us) { + prev_frame = now_time; + TestDraw(); + } + } + + TestShutdown(); + XDestroyWindow(display, window); + } +#elif defined(__APPLE__) + void RenderInterfaceTest(RenderInterface* renderInterface) { + assert(false); + } +#endif +}; \ No newline at end of file diff --git a/examples/shaders/RenderInterfaceTestCS.hlsl b/examples/shaders/RenderInterfaceTestCS.hlsl new file mode 100644 index 0000000..d148e69 --- /dev/null +++ b/examples/shaders/RenderInterfaceTestCS.hlsl @@ -0,0 +1,26 @@ +// +// RT64 +// + +struct Constants { + float4 Multiply; + uint2 Resolution; +}; + +[[vk::push_constant]] ConstantBuffer gConstants : register(b0); + +Texture2D gBlueNoiseTexture : register(t1); +SamplerState gSampler : register(s2); +[[vk::image_format("rgba8")]] +RWTexture2D gTarget : register(u16, space1); + +[numthreads(8, 8, 1)] +void CSMain(uint2 coord : SV_DispatchThreadID) { + if (any(coord >= gConstants.Resolution)) { + return; + } + + float2 blueNoiseUV = float2(coord) / float2(gConstants.Resolution); + float4 blueNoise = float4(gBlueNoiseTexture.SampleLevel(gSampler, blueNoiseUV, 0).rgb, 1.0f); + gTarget[coord] *= (blueNoise * gConstants.Multiply); +} \ No newline at end of file diff --git a/examples/shaders/RenderInterfaceTestPS.hlsl b/examples/shaders/RenderInterfaceTestPS.hlsl new file mode 100644 index 0000000..2f939f1 --- /dev/null +++ b/examples/shaders/RenderInterfaceTestPS.hlsl @@ -0,0 +1,19 @@ +// +// RT64 +// + +struct Constants { + float4 colorAdd; + uint textureIndex; +}; + +[[vk::push_constant]] ConstantBuffer gConstants : register(b0); + +SamplerState gSampler : register(s1); +Texture2D gTexture[] : register(t2); + +float4 PSMain(in float4 pos : SV_Position, in float2 uv : TEXCOORD) : SV_TARGET { + float4 result = float4(gTexture[NonUniformResourceIndex(gConstants.textureIndex)].SampleLevel(gSampler, uv, 0).rgb, 1.0f); + result += gConstants.colorAdd; + return result; +} \ No newline at end of file diff --git a/examples/shaders/RenderInterfaceTestPostPS.hlsl b/examples/shaders/RenderInterfaceTestPostPS.hlsl new file mode 100644 index 0000000..3fe5cff --- /dev/null +++ b/examples/shaders/RenderInterfaceTestPostPS.hlsl @@ -0,0 +1,11 @@ +// +// RT64 +// + +Texture2D gTexture : register(t1); +SamplerState gSampler : register(s2); + +float4 PSMain(in float4 pos : SV_Position, in float2 uv : TEXCOORD) : SV_TARGET { + float4 result = float4(gTexture.SampleLevel(gSampler, uv, 0).rgb, 1.0f); + return result; +} \ No newline at end of file diff --git a/examples/shaders/RenderInterfaceTestPostVS.hlsl b/examples/shaders/RenderInterfaceTestPostVS.hlsl new file mode 100644 index 0000000..b9bba7c --- /dev/null +++ b/examples/shaders/RenderInterfaceTestPostVS.hlsl @@ -0,0 +1,9 @@ +// +// RT64 +// + +void VSMain(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD0) { + uv.x = (id == 2) ? 2.0f : 0.0f; + uv.y = (id == 1) ? 2.0f : 0.0f; + pos = float4(uv * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 1.0f, 1.0f); +} \ No newline at end of file diff --git a/examples/shaders/RenderInterfaceTestRT.hlsl b/examples/shaders/RenderInterfaceTestRT.hlsl new file mode 100644 index 0000000..3da3688 --- /dev/null +++ b/examples/shaders/RenderInterfaceTestRT.hlsl @@ -0,0 +1,44 @@ +// +// RT64 +// + +struct Attributes { + float2 bary; +}; + +struct Payload { + float4 color; +}; + +struct BufferParams { + float3 rgbMultiplier; + uint3 pad; +}; + +RaytracingAccelerationStructure gBVH : register(t0); +RWTexture2D gOutput : register(u1); +StructuredBuffer gBufferParams : register(t2); + +[shader("raygeneration")] +void ColorRayGen() { + RayDesc ray; + ray.Origin = float3(float2(DispatchRaysIndex().xy) / float2(DispatchRaysDimensions().xy), 0.0f); + ray.Direction = float3(0.0f, 0.0f, 1.0f); + ray.TMin = 1e-6f; + ray.TMax = 1e6f; + + Payload payload; + payload.color = float4(0.0f, 0.5f, 0.0f, 1.0f); + TraceRay(gBVH, RAY_FLAG_NONE, 0xFF, 0, 0, 0, ray, payload); + gOutput[DispatchRaysIndex().xy] = payload.color; +} + +[shader("closesthit")] +void ColorClosestHit(inout Payload payload, in Attributes attr) { + payload.color = float4(1.0f, attr.bary.xy, 1.0f) * float4(gBufferParams[0].rgbMultiplier, 1.0f); +} + +[shader("miss")] +void ColorMiss(inout Payload payload) { + payload.color = float4(0.25f, 0.25f, 0.5f, 1.0f) * float4(gBufferParams[1].rgbMultiplier, 1.0f); +} \ No newline at end of file diff --git a/examples/shaders/RenderInterfaceTestVS.hlsl b/examples/shaders/RenderInterfaceTestVS.hlsl new file mode 100644 index 0000000..ae45140 --- /dev/null +++ b/examples/shaders/RenderInterfaceTestVS.hlsl @@ -0,0 +1,8 @@ +// +// RT64 +// + +void VSMain(in float2 vertexPos : POSITION, in float2 vertexUV : TEXCOORD, out float4 pos : SV_POSITION, out float2 uv : TEXCOORD) { + pos = float4(vertexPos, 1.0f, 1.0f); + uv = vertexUV; +} \ No newline at end of file diff --git a/src/common/rt64_thread.cpp b/src/common/rt64_thread.cpp index 3af7910..bcf82ad 100644 --- a/src/common/rt64_thread.cpp +++ b/src/common/rt64_thread.cpp @@ -45,6 +45,8 @@ namespace RT64 { SetThreadDescription(GetCurrentThread(), nameWide.c_str()); # elif defined(__linux__) pthread_setname_np(pthread_self(), str.c_str()); +# elif defined(__APPLE__) + pthread_setname_np(str.c_str()); # else static_assert(false, "Unimplemented"); # endif @@ -53,7 +55,7 @@ namespace RT64 { void Thread::setCurrentThreadPriority(Priority priority) { # if defined(_WIN32) SetThreadPriority(GetCurrentThread(), toWindowsPriority(priority)); -# elif defined(__linux__) +# elif defined(__linux__) || defined(__APPLE__) // On Linux, thread priorities can't be changed under the default scheduler policy (SCHED_OTHER) and the other policies // that are available without root privileges are lower priority. Instead you can set the thread's "nice" value, which ranges // from -20 to 19 (lower being higher priority). However, by strict POSIX spec "nice" is meant to be per-process instead of diff --git a/src/contrib/dxc b/src/contrib/dxc index 46e63bc..9d592df 160000 --- a/src/contrib/dxc +++ b/src/contrib/dxc @@ -1 +1 @@ -Subproject commit 46e63bcacad9e20e97f221fe492482d84a88210c +Subproject commit 9d592dfcb0aac9493865f253a855b558820be06d diff --git a/src/hle/rt64_application_window.cpp b/src/hle/rt64_application_window.cpp index 3f407cd..8db29fe 100644 --- a/src/hle/rt64_application_window.cpp +++ b/src/hle/rt64_application_window.cpp @@ -89,7 +89,7 @@ namespace RT64 { bounds.height = rect.bottom - rect.top; # elif defined(__ANDROID__) static_assert(false && "Android unimplemented"); -# elif defined(__linux__) +# elif defined(__linux__) || defined(__APPLE__) if (SDL_VideoInit(nullptr) != 0) { printf("Failed to init SDL2 video: %s\n", SDL_GetError()); assert(false && "Failed to init SDL2 video"); @@ -123,6 +123,8 @@ namespace RT64 { # elif defined(__linux__) windowHandle.display = wmInfo.info.x11.display; windowHandle.window = wmInfo.info.x11.window; +# elif defined(__APPLE__) + windowHandle.window = wmInfo.info.cocoa.window; # else static_assert(false && "Android unimplemented"); # endif @@ -133,6 +135,10 @@ namespace RT64 { # ifdef _WIN32 setup(windowHandle, listener, GetCurrentThreadId()); +# elif defined(__APPLE__) + uint64_t tid; + pthread_threadid_np(nullptr, &tid); + setup(windowHandle, listener, tid); # else setup(windowHandle, listener, pthread_self()); # endif diff --git a/src/render/rt64_buffer_uploader.cpp b/src/render/rt64_buffer_uploader.cpp index fa91ef3..d0979ce 100644 --- a/src/render/rt64_buffer_uploader.cpp +++ b/src/render/rt64_buffer_uploader.cpp @@ -3,6 +3,7 @@ // #include +#include #include "common/rt64_thread.h" @@ -91,7 +92,7 @@ namespace RT64 { // Recreate the buffer pair. const uint64_t BlockAlignment = 256; - bufferPair.allocatedSize = std::max((requiredSize * 3) / 2, BlockAlignment); + bufferPair.allocatedSize = std::max(((uint64_t)requiredSize * 3) / 2, BlockAlignment); bufferPair.allocatedSize = roundUp(bufferPair.allocatedSize, BlockAlignment); bufferPair.uploadBuffer = worker->device->createBuffer(RenderBufferDesc::UploadBuffer(bufferPair.allocatedSize)); bufferPair.defaultBuffer = worker->device->createBuffer(RenderBufferDesc::DefaultBuffer(bufferPair.allocatedSize, u.bufferFlags)); diff --git a/src/rhi/rt64_render_interface_types.h b/src/rhi/rt64_render_interface_types.h index 9b7419d..61e8ed1 100644 --- a/src/rhi/rt64_render_interface_types.h +++ b/src/rhi/rt64_render_interface_types.h @@ -20,6 +20,8 @@ #undef None #undef Status #undef LockMask +#elif defined(__APPLE__) +typedef struct _NSWindow NSWindow; #endif namespace RT64 { @@ -37,6 +39,14 @@ namespace RT64 { } bool operator!=(const struct RenderWindow& rhs) const { return !(*this == rhs); } }; +#elif defined(__APPLE__) + struct RenderWindow { + NSWindow* window; + bool operator==(const struct RenderWindow& rhs) const { + return window == rhs.window; + } + bool operator!=(const struct RenderWindow& rhs) const { return !(*this == rhs); } + }; #else static_assert(false, "RenderWindow was not defined for this platform."); #endif