diff --git a/CMakeLists.txt b/CMakeLists.txt index 00a9276..d23c462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -446,6 +446,7 @@ build_compute_shader(rt64 "src/shaders/IdleCS.hlsl") build_compute_shader(rt64 "src/shaders/LuminanceHistogramCS.hlsl") build_pixel_shader( rt64 "src/shaders/PostBlendDitherNoisePS.hlsl" "src/shaders/PostBlendDitherNoiseAddPS.hlsl" "-D ADD_MODE") build_pixel_shader( rt64 "src/shaders/PostBlendDitherNoisePS.hlsl" "src/shaders/PostBlendDitherNoiseSubPS.hlsl" "-D SUB_MODE") +build_pixel_shader( rt64 "src/shaders/PostBlendDitherNoisePS.hlsl" "src/shaders/PostBlendDitherNoiseSubNegativePS.hlsl" "-D SUB_MODE" "-D NEGATIVE_MODE") build_compute_shader(rt64 "src/shaders/RSPModifyCS.hlsl") build_compute_shader(rt64 "src/shaders/RSPProcessCS.hlsl") build_compute_shader(rt64 "src/shaders/RSPWorldCS.hlsl") diff --git a/src/common/rt64_emulator_configuration.cpp b/src/common/rt64_emulator_configuration.cpp index 2b1f682..303113d 100644 --- a/src/common/rt64_emulator_configuration.cpp +++ b/src/common/rt64_emulator_configuration.cpp @@ -9,6 +9,7 @@ namespace RT64 { EmulatorConfiguration::EmulatorConfiguration() { dither.postBlendNoise = true; + dither.postBlendNoiseNegative = false; framebuffer.renderToRAM = true; framebuffer.copyWithGPU = true; } diff --git a/src/common/rt64_emulator_configuration.h b/src/common/rt64_emulator_configuration.h index 71fe295..1fdd873 100644 --- a/src/common/rt64_emulator_configuration.h +++ b/src/common/rt64_emulator_configuration.h @@ -10,6 +10,7 @@ namespace RT64 { struct EmulatorConfiguration { struct Dither { bool postBlendNoise; + bool postBlendNoiseNegative; }; struct Framebuffer { diff --git a/src/hle/rt64_state.cpp b/src/hle/rt64_state.cpp index 8128ab3..5e574ff 100644 --- a/src/hle/rt64_state.cpp +++ b/src/hle/rt64_state.cpp @@ -1217,6 +1217,7 @@ namespace RT64 { drawParams.ubershadersOnly = false; drawParams.fixRectLR = false; drawParams.postBlendNoise = ext.emulatorConfig->dither.postBlendNoise; + drawParams.postBlendNoiseNegative = ext.emulatorConfig->dither.postBlendNoiseNegative; drawParams.maxGameCall = UINT_MAX; framebufferRenderer->addFramebuffer(drawParams); } @@ -2090,6 +2091,7 @@ namespace RT64 { ImGui::Text("Dither"); ImGui::Indent(); emulatorConfigChanged = ImGui::Checkbox("Post Blend Noise", &emulatorConfig.dither.postBlendNoise) || emulatorConfigChanged; + emulatorConfigChanged = ImGui::Checkbox("Post Blend Noise Negative", &emulatorConfig.dither.postBlendNoiseNegative) || emulatorConfigChanged; ImGui::Unindent(); ImGui::Text("Framebuffer"); ImGui::Indent(); diff --git a/src/hle/rt64_workload_queue.cpp b/src/hle/rt64_workload_queue.cpp index 5511fd5..144dadb 100644 --- a/src/hle/rt64_workload_queue.cpp +++ b/src/hle/rt64_workload_queue.cpp @@ -248,6 +248,7 @@ namespace RT64 { workloadConfig.fixRectLR = ext.sharedResources->enhancementConfig.rect.fixRectLR; workloadConfig.postBlendNoise = ext.sharedResources->emulatorConfig.dither.postBlendNoise; + workloadConfig.postBlendNoiseNegative = ext.sharedResources->emulatorConfig.dither.postBlendNoiseNegative; if (ext.sharedResources->fbConfigChanged || sizeChanged) { { @@ -588,6 +589,7 @@ namespace RT64 { drawParams.ubershadersOnly = ubershadersOnly; drawParams.fixRectLR = workloadConfig.fixRectLR; drawParams.postBlendNoise = workloadConfig.postBlendNoise; + drawParams.postBlendNoiseNegative = workloadConfig.postBlendNoiseNegative; drawParams.maxGameCall = std::min(gameCallCountMax - gameCallCursor, fbPair.gameCallCount); framebufferRenderer->addFramebuffer(drawParams); } diff --git a/src/hle/rt64_workload_queue.h b/src/hle/rt64_workload_queue.h index 1e54ff0..e9d7a02 100644 --- a/src/hle/rt64_workload_queue.h +++ b/src/hle/rt64_workload_queue.h @@ -57,6 +57,7 @@ namespace RT64 { uint32_t targetRate = 0; bool fixRectLR = false; bool postBlendNoise = false; + bool postBlendNoiseNegative = false; }; External ext; diff --git a/src/render/rt64_framebuffer_renderer.cpp b/src/render/rt64_framebuffer_renderer.cpp index 0facb1e..8c17e5e 100644 --- a/src/render/rt64_framebuffer_renderer.cpp +++ b/src/render/rt64_framebuffer_renderer.cpp @@ -580,10 +580,18 @@ namespace RT64 { worker->commandList->setGraphicsPushConstants(0, &rasterParams); drawCallTriangles(drawCall); + // Simulate dither noise. if (triangles.postBlendDitherNoise) { - worker->commandList->setPipeline(postBlendDitherNoiseAddPipeline); - drawCallTriangles(drawCall); - worker->commandList->setPipeline(postBlendDitherNoiseSubPipeline); + if (triangles.postBlendDitherNoiseNegative) { + worker->commandList->setPipeline(postBlendDitherNoiseSubNegativePipeline); + } + else { + worker->commandList->setPipeline(postBlendDitherNoiseAddPipeline); + drawCallTriangles(drawCall); + + worker->commandList->setPipeline(postBlendDitherNoiseSubPipeline); + } + drawCallTriangles(drawCall); previousPipeline = nullptr; } @@ -1313,12 +1321,15 @@ namespace RT64 { framebuffer.descDummyFbSet->setTexture(framebuffer.descDummyFbSet->gBackgroundColor, p.fbStorage->colorTarget->getResolvedTexture(), RenderTextureLayout::SHADER_READ, p.fbStorage->colorTarget->getResolvedTextureView()); framebuffer.descDummyFbSet->setTexture(framebuffer.descDummyFbSet->gBackgroundDepth, dummyDepthTarget.get(), RenderTextureLayout::DEPTH_READ, dummyDepthTargetView.get()); - // Make a new render target draw call. - RenderTargetDrawCall &targetDrawCall = framebuffer.renderTargetDrawCall; + // Store ubershader and other effect pipelines. const RasterShaderUber *rasterShaderUber = p.rasterShaderCache->getGPUShaderUber(); rendererPipelineLayout = rasterShaderUber->pipelineLayout.get(); postBlendDitherNoiseAddPipeline = rasterShaderUber->postBlendDitherNoiseAddPipeline.get(); postBlendDitherNoiseSubPipeline = rasterShaderUber->postBlendDitherNoiseSubPipeline.get(); + postBlendDitherNoiseSubNegativePipeline = rasterShaderUber->postBlendDitherNoiseSubNegativePipeline.get(); + + // Make a new render target draw call. + RenderTargetDrawCall &targetDrawCall = framebuffer.renderTargetDrawCall; const DrawData &drawData = p.curWorkload->drawData; const DrawBuffers &drawBuffers = p.curWorkload->drawBuffers; const OutputBuffers &outputBuffers = p.curWorkload->outputBuffers; @@ -1609,6 +1620,7 @@ namespace RT64 { // Indicate if post blend dither noise should be applied. bool rgbDitherNoise = (call.shaderDesc.otherMode.rgbDither() == G_CD_NOISE); triangles.postBlendDitherNoise = rgbDitherNoise && !call.shaderDesc.otherMode.zCmp() && !call.shaderDesc.otherMode.zUpd(); + triangles.postBlendDitherNoiseNegative = p.postBlendNoiseNegative; } break; diff --git a/src/render/rt64_framebuffer_renderer.h b/src/render/rt64_framebuffer_renderer.h index 1cc4fdc..6e6590c 100644 --- a/src/render/rt64_framebuffer_renderer.h +++ b/src/render/rt64_framebuffer_renderer.h @@ -6,6 +6,7 @@ #include +#include "common/rt64_emulator_configuration.h" #include "common/rt64_user_configuration.h" #include "hle/rt64_framebuffer_manager.h" #include "hle/rt64_workload.h" @@ -85,6 +86,7 @@ namespace RT64 { RenderPipelineLayout *rendererPipelineLayout = nullptr; RenderPipeline *postBlendDitherNoiseAddPipeline = nullptr; RenderPipeline *postBlendDitherNoiseSubPipeline = nullptr; + RenderPipeline *postBlendDitherNoiseSubNegativePipeline = nullptr; std::unique_ptr descCommonSet; std::unique_ptr descTextureSet; std::unique_ptr dummyDepthTarget; @@ -140,6 +142,7 @@ namespace RT64 { bool ubershadersOnly; bool fixRectLR; bool postBlendNoise; + bool postBlendNoiseNegative; uint32_t maxGameCall; }; diff --git a/src/render/rt64_framebuffer_renderer_call.h b/src/render/rt64_framebuffer_renderer_call.h index c216b31..19b950e 100644 --- a/src/render/rt64_framebuffer_renderer_call.h +++ b/src/render/rt64_framebuffer_renderer_call.h @@ -37,8 +37,9 @@ namespace RT64 { RenderViewport viewport; uint32_t indexStart; uint32_t faceCount; - bool vertexTestZ; - bool postBlendDitherNoise; + bool vertexTestZ : 1; + bool postBlendDitherNoise : 1; + bool postBlendDitherNoiseNegative : 1; } triangles; struct { diff --git a/src/render/rt64_raster_shader.cpp b/src/render/rt64_raster_shader.cpp index f2022e3..dce1af2 100644 --- a/src/render/rt64_raster_shader.cpp +++ b/src/render/rt64_raster_shader.cpp @@ -20,6 +20,7 @@ #include "shaders/RasterVSSpecConstantFlat.hlsl.spirv.h" #include "shaders/PostBlendDitherNoiseAddPS.hlsl.spirv.h" #include "shaders/PostBlendDitherNoiseSubPS.hlsl.spirv.h" +#include "shaders/PostBlendDitherNoiseSubNegativePS.hlsl.spirv.h" #ifdef _WIN32 # include "shaders/RasterPSLibrary.hlsl.dxil.h" # include "shaders/RasterPSLibraryMS.hlsl.dxil.h" @@ -29,6 +30,7 @@ # include "shaders/RasterVSDynamic.hlsl.dxil.h" # include "shaders/PostBlendDitherNoiseAddPS.hlsl.dxil.h" # include "shaders/PostBlendDitherNoiseSubPS.hlsl.dxil.h" +# include "shaders/PostBlendDitherNoiseSubNegativePS.hlsl.dxil.h" #endif #include "shared/rt64_raster_params.h" @@ -463,16 +465,19 @@ namespace RT64 { // Create the pipelines for post blend operations. std::unique_ptr postBlendAddPixelShader; std::unique_ptr postBlendSubPixelShader; + std::unique_ptr postBlendSubNegativePixelShader; switch (shaderFormat) { # ifdef _WIN32 case RenderShaderFormat::DXIL: postBlendAddPixelShader = device->createShader(PostBlendDitherNoiseAddPSBlobDXIL, std::size(PostBlendDitherNoiseAddPSBlobDXIL), "PSMain", shaderFormat); postBlendSubPixelShader = device->createShader(PostBlendDitherNoiseSubPSBlobDXIL, std::size(PostBlendDitherNoiseSubPSBlobDXIL), "PSMain", shaderFormat); + postBlendSubNegativePixelShader = device->createShader(PostBlendDitherNoiseSubNegativePSBlobDXIL, std::size(PostBlendDitherNoiseSubNegativePSBlobDXIL), "PSMain", shaderFormat); break; # endif case RenderShaderFormat::SPIRV: postBlendAddPixelShader = device->createShader(PostBlendDitherNoiseAddPSBlobSPIRV, std::size(PostBlendDitherNoiseAddPSBlobSPIRV), "PSMain", shaderFormat); postBlendSubPixelShader = device->createShader(PostBlendDitherNoiseSubPSBlobSPIRV, std::size(PostBlendDitherNoiseSubPSBlobSPIRV), "PSMain", shaderFormat); + postBlendSubNegativePixelShader = device->createShader(PostBlendDitherNoiseSubNegativePSBlobSPIRV, std::size(PostBlendDitherNoiseSubNegativePSBlobSPIRV), "PSMain", shaderFormat); break; default: assert(false && "Unknown shader format."); @@ -509,6 +514,9 @@ namespace RT64 { postBlendDesc.pixelShader = postBlendSubPixelShader.get(); targetBlend.blendOp = RenderBlendOperation::REV_SUBTRACT; postBlendDitherNoiseSubPipeline = device->createGraphicsPipeline(postBlendDesc); + + postBlendDesc.pixelShader = postBlendSubNegativePixelShader.get(); + postBlendDitherNoiseSubNegativePipeline = device->createGraphicsPipeline(postBlendDesc); } RasterShaderUber::~RasterShaderUber() { diff --git a/src/render/rt64_raster_shader.h b/src/render/rt64_raster_shader.h index 767c09d..e54229b 100644 --- a/src/render/rt64_raster_shader.h +++ b/src/render/rt64_raster_shader.h @@ -79,6 +79,7 @@ namespace RT64 { std::unique_ptr pipelines[8]; std::unique_ptr postBlendDitherNoiseAddPipeline; std::unique_ptr postBlendDitherNoiseSubPipeline; + std::unique_ptr postBlendDitherNoiseSubNegativePipeline; std::mutex firstPipelineMutex; std::condition_variable firstPipelineCondition; bool pipelinesCreated = false; diff --git a/src/shaders/PostBlendDitherNoisePS.hlsl b/src/shaders/PostBlendDitherNoisePS.hlsl index c2601b5..0efa3d5 100644 --- a/src/shaders/PostBlendDitherNoisePS.hlsl +++ b/src/shaders/PostBlendDitherNoisePS.hlsl @@ -20,12 +20,18 @@ void PSMain( int2 pixelPosSeed = floor(vertexPosition.xy); uint randomSeed = initRand(FrParams.frameCount, gConstants.renderIndex * (pixelPosSeed.y * 65536 + pixelPosSeed.x), 16); const float Range = (7.0f * FrParams.ditherNoiseStrength) / 255.0f; - const float HalfRange = Range / 2.0f; - resultColor.r = nextRand(randomSeed) * Range - HalfRange; - resultColor.g = nextRand(randomSeed) * Range - HalfRange; - resultColor.b = nextRand(randomSeed) * Range - HalfRange; + resultColor.r = nextRand(randomSeed) * Range; + resultColor.g = nextRand(randomSeed) * Range; + resultColor.b = nextRand(randomSeed) * Range; resultColor.a = 0.0f; +#if defined(NEGATIVE_MODE) + resultColor.rgb -= Range; +#else + const float HalfRange = Range / 2.0f; + resultColor.rgb -= HalfRange; +#endif + #if defined(ADD_MODE) resultColor = max(resultColor, 0.0f); #elif defined(SUB_MODE)