Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:ahmedmoselhi2:branches:Emulators
melonds
melonds_compute-shader-renderer.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File melonds_compute-shader-renderer.patch of Package melonds
From 924415716341d244996f20baba8883ecc26dcc7e Mon Sep 17 00:00:00 2001 From: v-fox <virtuousfox@gmail.com> Date: Tue, 31 Oct 2023 16:33:56 +0500 Subject: [PATCH] compute shader renderer (squashed) --- src/CMakeLists.txt | 5 + src/DMA.cpp | 1 + src/DSi_NDMA.cpp | 1 + src/GPU.cpp | 8 +- src/GPU.h | 13 +- src/GPU2D.cpp | 1 + src/GPU2D_Soft.cpp | 3 +- src/GPU3D.cpp | 1 + src/GPU3D.h | 5 + src/GPU3D_Compute.cpp | 1037 ++++++++++++ src/GPU3D_Compute.h | 227 +++ src/GPU3D_Compute_shaders.h | 1665 +++++++++++++++++++ src/GPU3D_OpenGL.cpp | 210 +-- src/GPU3D_OpenGL.h | 26 +- src/GPU3D_Texcache.cpp | 269 +++ src/GPU3D_Texcache.h | 309 ++++ src/GPU3D_TexcacheOpenGL.cpp | 29 + src/GPU3D_TexcacheOpenGL.h | 25 + src/GPU_OpenGL.cpp | 45 +- src/GPU_OpenGL.h | 6 +- src/NDS.cpp | 1 + src/NonStupidBitfield.h | 66 + src/OpenGLSupport.cpp | 330 +++- src/OpenGLSupport.h | 21 +- src/frontend/qt_sdl/Config.cpp | 5 +- src/frontend/qt_sdl/Config.h | 1 + src/frontend/qt_sdl/OSD.cpp | 27 +- src/frontend/qt_sdl/VideoSettingsDialog.cpp | 61 +- src/frontend/qt_sdl/VideoSettingsDialog.h | 3 + src/frontend/qt_sdl/VideoSettingsDialog.ui | 90 +- src/frontend/qt_sdl/main.cpp | 40 +- src/frontend/qt_sdl/main.h | 2 +- 32 files changed, 4189 insertions(+), 344 deletions(-) create mode 100644 src/GPU3D_Compute.cpp create mode 100644 src/GPU3D_Compute.h create mode 100644 src/GPU3D_Compute_shaders.h create mode 100644 src/GPU3D_Texcache.cpp create mode 100644 src/GPU3D_Texcache.h create mode 100644 src/GPU3D_TexcacheOpenGL.cpp create mode 100644 src/GPU3D_TexcacheOpenGL.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9fe93ae..a02fcaf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,8 @@ add_library(core STATIC GPU2D_Soft.cpp GPU3D.cpp GPU3D_Soft.cpp + GPU3D_Texcache.cpp + GPU3D_Texcache.h melonDLDI.h NDS.cpp NDSCart.cpp @@ -76,6 +78,9 @@ if (ENABLE_OGLRENDERER) GPU_OpenGL.cpp GPU_OpenGL_shaders.h GPU3D_OpenGL.cpp + GPU3D_Compute.cpp + GPU3D_TexcacheOpenGL.cpp + GPU3D_TexcacheOpenGL.h GPU3D_OpenGL_shaders.h OpenGLSupport.cpp) diff --git a/src/DMA.cpp b/src/DMA.cpp index a7558ff..51a5ea8 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -21,6 +21,7 @@ #include "DSi.h" #include "DMA.h" #include "GPU.h" +#include "GPU3D.h" #include "DMA_Timings.h" #include "Platform.h" diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index dba920c..4db18f3 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -22,6 +22,7 @@ #include "DSi_NDMA.h" #include "GPU.h" #include "DSi_AES.h" +#include "GPU3D.h" using Platform::Log; using Platform::LogLevel; diff --git a/src/GPU.cpp b/src/GPU.cpp index 630e88d..bc822c7 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -25,6 +25,7 @@ #endif #include "GPU2D_Soft.h" +#include "GPU3D.h" using Platform::Log; using Platform::LogLevel; @@ -395,7 +396,7 @@ void AssignFramebuffers() void InitRenderer(int renderer) { #ifdef OGLRENDERER_ENABLED - if (renderer == 1) + if (renderer != renderer3D_Software) { CurGLCompositor = GLCompositor::New(); // Create opengl renderer @@ -405,7 +406,10 @@ void InitRenderer(int renderer) renderer = 0; GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>(); } - GPU3D::CurrentRenderer = GPU3D::GLRenderer::New(); + if (renderer == renderer3D_OpenGL) + GPU3D::CurrentRenderer = GPU3D::GLRenderer::New(); + else if (renderer == renderer3D_OpenGLCompute) + GPU3D::CurrentRenderer = GPU3D::ComputeRenderer::New(); if (!GPU3D::CurrentRenderer) { // Fallback on software renderer diff --git a/src/GPU.h b/src/GPU.h index cec8a2d..b4692b6 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -160,6 +160,7 @@ struct RenderSettings int GL_ScaleFactor; bool GL_BetterPolygons; + bool GL_HiresCoordinates; }; @@ -170,6 +171,16 @@ void Stop(); void DoSavestate(Savestate* file); +enum +{ + renderer3D_Software = 0, +#ifdef OGLRENDERER_ENABLED + renderer3D_OpenGL, + renderer3D_OpenGLCompute, +#endif + renderer3D_Max, +}; + void InitRenderer(int renderer); void DeInitRenderer(); void ResetRenderer(); @@ -618,6 +629,4 @@ void SetDispStat(u32 cpu, u16 val); void SetVCount(u16 val); } -#include "GPU3D.h" - #endif diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index d5df992..fe01ebb 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -20,6 +20,7 @@ #include <string.h> #include "NDS.h" #include "GPU.h" +#include "GPU3D.h" using Platform::Log; using Platform::LogLevel; diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp index 070079a..3704de9 100644 --- a/src/GPU2D_Soft.cpp +++ b/src/GPU2D_Soft.cpp @@ -18,6 +18,7 @@ #include "GPU2D_Soft.h" #include "GPU.h" +#include "GPU3D.h" namespace GPU2D { @@ -367,7 +368,7 @@ void SoftRenderer::VBlankEnd(Unit* unitA, Unit* unitB) { if ((unitA->CaptureCnt & (1<<31)) && (((unitA->CaptureCnt >> 29) & 0x3) != 1)) { - reinterpret_cast<GPU3D::GLRenderer*>(GPU3D::CurrentRenderer.get())->PrepareCaptureFrame(); + GPU3D::CurrentRenderer.get()->PrepareCaptureFrame(); } } #endif diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 8cc380a..10b3dce 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -23,6 +23,7 @@ #include "GPU.h" #include "FIFO.h" #include "Platform.h" +#include "GPU3D.h" using Platform::Log; using Platform::LogLevel; diff --git a/src/GPU3D.h b/src/GPU3D.h index 44d422a..59c958d 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -156,6 +156,10 @@ public: virtual void RenderFrame() = 0; virtual void RestartFrame() {}; virtual u32* GetLine(int line) = 0; + + virtual void SetupAccelFrame() {} + virtual void PrepareCaptureFrame() {} + protected: Renderer3D(bool Accelerated); }; @@ -169,6 +173,7 @@ extern std::unique_ptr<Renderer3D> CurrentRenderer; #ifdef OGLRENDERER_ENABLED #include "GPU3D_OpenGL.h" +#include "GPU3D_Compute.h" #endif #endif diff --git a/src/GPU3D_Compute.cpp b/src/GPU3D_Compute.cpp new file mode 100644 index 0000000..8d500a4 --- /dev/null +++ b/src/GPU3D_Compute.cpp @@ -0,0 +1,1037 @@ +/* + Copyright 2016-2022 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include "GPU3D_Compute.h" + +#include <assert.h> + +#include "OpenGLSupport.h" + +#include "GPU3D_Compute_shaders.h" + +namespace GPU3D +{ + +ComputeRenderer::ComputeRenderer() + : Renderer3D(true), Texcache(TexcacheOpenGLLoader()) +{} + +bool ComputeRenderer::CompileShader(GLuint& shader, const std::string& source, const std::initializer_list<const char*>& defines) +{ + std::string shaderName; + std::string shaderSource; + shaderSource += "#version 430 core\n"; + for (const char* define : defines) + { + shaderSource += "#define "; + shaderSource += define; + shaderSource += '\n'; + shaderName += define; + shaderName += ','; + } + shaderSource += "#define ScreenWidth "; + shaderSource += std::to_string(ScreenWidth); + shaderSource += "\n#define ScreenHeight "; + shaderSource += std::to_string(ScreenHeight); + shaderSource += "\n#define MaxWorkTiles "; + shaderSource += std::to_string(MaxWorkTiles); + + shaderSource += ComputeRendererShaders::Common; + shaderSource += source; + + return OpenGL::CompileComputeProgram(shader, shaderSource.c_str(), shaderName.c_str()); +} + +void blah(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam) +{ + printf("%s\n", message); +} + +std::unique_ptr<ComputeRenderer> ComputeRenderer::New() +{ + std::unique_ptr<ComputeRenderer> result = std::unique_ptr<ComputeRenderer>(new ComputeRenderer()); + + //glDebugMessageCallback(blah, NULL); + //glEnable(GL_DEBUG_OUTPUT); + glGenBuffers(1, &result->YSpanSetupMemory); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, result->YSpanSetupMemory); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(SpanSetupY)*MaxYSpanSetups, nullptr, GL_DYNAMIC_DRAW); + + glGenBuffers(1, &result->RenderPolygonMemory); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, result->RenderPolygonMemory); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(RenderPolygon)*2048, nullptr, GL_DYNAMIC_DRAW); + + glGenBuffers(1, &result->XSpanSetupMemory); + glGenBuffers(1, &result->BinResultMemory); + glGenBuffers(1, &result->FinalTileMemory); + glGenBuffers(1, &result->YSpanIndicesTextureMemory); + glGenBuffers(tilememoryLayer_Num, result->TileMemory); + glGenBuffers(1, &result->WorkDescMemory); + + glGenTextures(1, &result->YSpanIndicesTexture); + glGenTextures(1, &result->LowResFramebuffer); + glBindTexture(GL_TEXTURE_2D, result->LowResFramebuffer); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8UI, 256, 192); + + glGenBuffers(1, &result->MetaUniformMemory); + glBindBuffer(GL_UNIFORM_BUFFER, result->MetaUniformMemory); + glBufferData(GL_UNIFORM_BUFFER, sizeof(MetaUniform), nullptr, GL_DYNAMIC_DRAW); + + glGenSamplers(9, result->Samplers); + for (u32 j = 0; j < 3; j++) + { + for (u32 i = 0; i < 3; i++) + { + const GLenum translateWrapMode[3] = {GL_CLAMP_TO_EDGE, GL_REPEAT, GL_MIRRORED_REPEAT}; + glSamplerParameteri(result->Samplers[i+j*3], GL_TEXTURE_WRAP_S, translateWrapMode[i]); + glSamplerParameteri(result->Samplers[i+j*3], GL_TEXTURE_WRAP_T, translateWrapMode[j]); + glSamplerParameteri(result->Samplers[i+j*3], GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameterf(result->Samplers[i+j*3], GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + } + + glGenBuffers(1, &result->PixelBuffer); + glBindBuffer(GL_PIXEL_PACK_BUFFER, result->PixelBuffer); + glBufferData(GL_PIXEL_PACK_BUFFER, 256*192*4, NULL, GL_DYNAMIC_READ); + + return result; +} + +ComputeRenderer::~ComputeRenderer() +{ + Texcache.Reset(); + + glDeleteBuffers(1, &YSpanSetupMemory); + glDeleteBuffers(1, &RenderPolygonMemory); + glDeleteBuffers(1, &XSpanSetupMemory); + glDeleteBuffers(1, &BinResultMemory); + glDeleteBuffers(tilememoryLayer_Num, TileMemory); + glDeleteBuffers(1, &WorkDescMemory); + glDeleteBuffers(1, &FinalTileMemory); + glDeleteBuffers(1, &YSpanIndicesTextureMemory); + glDeleteTextures(1, &YSpanIndicesTexture); + glDeleteTextures(1, &Framebuffer); + glDeleteBuffers(1, &MetaUniformMemory); + + glDeleteSamplers(9, Samplers); + glDeleteBuffers(1, &PixelBuffer); +} + +void ComputeRenderer::DeleteShaders() +{ + std::initializer_list<GLuint> allPrograms = + { + ShaderInterpXSpans[0], + ShaderInterpXSpans[1], + ShaderBinCombined, + ShaderDepthBlend[0], + ShaderDepthBlend[1], + ShaderRasteriseNoTexture[0], + ShaderRasteriseNoTexture[1], + ShaderRasteriseNoTextureToon[0], + ShaderRasteriseNoTextureToon[1], + ShaderRasteriseNoTextureHighlight[0], + ShaderRasteriseNoTextureHighlight[1], + ShaderRasteriseUseTextureDecal[0], + ShaderRasteriseUseTextureDecal[1], + ShaderRasteriseUseTextureModulate[0], + ShaderRasteriseUseTextureModulate[1], + ShaderRasteriseUseTextureToon[0], + ShaderRasteriseUseTextureToon[1], + ShaderRasteriseUseTextureHighlight[0], + ShaderRasteriseUseTextureHighlight[1], + ShaderRasteriseShadowMask[0], + ShaderRasteriseShadowMask[1], + ShaderClearCoarseBinMask, + ShaderClearIndirectWorkCount, + ShaderCalculateWorkListOffset, + ShaderSortWork, + ShaderFinalPass[0], + ShaderFinalPass[1], + ShaderFinalPass[2], + ShaderFinalPass[3], + ShaderFinalPass[4], + ShaderFinalPass[5], + ShaderFinalPass[6], + ShaderFinalPass[7], + }; + for (GLuint program : allPrograms) + glDeleteProgram(program); +} + +void ComputeRenderer::Reset() +{ + Texcache.Reset(); +} + +void ComputeRenderer::SetRenderSettings(GPU::RenderSettings& settings) +{ + if (ScaleFactor != -1) + { + DeleteShaders(); + } + + ScaleFactor = settings.GL_ScaleFactor; + ScreenWidth = 256 * ScaleFactor; + ScreenHeight = 192 * ScaleFactor; + + TilesPerLine = ScreenWidth/TileSize; + TileLines = ScreenHeight/TileSize; + + HiresCoordinates = settings.GL_HiresCoordinates; + + MaxWorkTiles = TilesPerLine*TileLines*8; + + for (int i = 0; i < tilememoryLayer_Num; i++) + { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, TileMemory[i]); + glBufferData(GL_SHADER_STORAGE_BUFFER, 4*TileSize*TileSize*MaxWorkTiles, nullptr, GL_DYNAMIC_DRAW); + } + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, FinalTileMemory); + glBufferData(GL_SHADER_STORAGE_BUFFER, 4*3*2*ScreenWidth*ScreenHeight, nullptr, GL_DYNAMIC_DRAW); + + int binResultSize = sizeof(BinResultHeader) + + TilesPerLine*TileLines*CoarseBinStride*4 // BinnedMaskCoarse + + TilesPerLine*TileLines*BinStride*4 // BinnedMask + + TilesPerLine*TileLines*BinStride*4; // WorkOffsets + glBindBuffer(GL_SHADER_STORAGE_BUFFER, BinResultMemory); + glBufferData(GL_SHADER_STORAGE_BUFFER, binResultSize, nullptr, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, WorkDescMemory); + glBufferData(GL_SHADER_STORAGE_BUFFER, MaxWorkTiles*2*4*2, nullptr, GL_DYNAMIC_DRAW); + + if (Framebuffer != 0) + glDeleteTextures(1, &Framebuffer); + glGenTextures(1, &Framebuffer); + glBindTexture(GL_TEXTURE_2D, Framebuffer); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, ScreenWidth, ScreenHeight); + + // eh those are pretty bad guesses + // though real hw shouldn't be eable to render all 2048 polygons on every line either + int maxYSpanIndices = 64*2048 * ScaleFactor; + YSpanIndices.resize(maxYSpanIndices); + + glBindBuffer(GL_TEXTURE_BUFFER, YSpanIndicesTextureMemory); + glBufferData(GL_TEXTURE_BUFFER, maxYSpanIndices*2*4, nullptr, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, XSpanSetupMemory); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(SpanSetupX)*maxYSpanIndices, nullptr, GL_DYNAMIC_DRAW); + + glBindTexture(GL_TEXTURE_BUFFER, YSpanIndicesTexture); + glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA16UI, YSpanIndicesTextureMemory); + + CompileShader(ShaderInterpXSpans[0], ComputeRendererShaders::InterpSpans, {"InterpSpans", "ZBuffer"}); + CompileShader(ShaderInterpXSpans[1], ComputeRendererShaders::InterpSpans, {"InterpSpans", "WBuffer"}); + CompileShader(ShaderBinCombined, ComputeRendererShaders::BinCombined, {"BinCombined"}); + CompileShader(ShaderDepthBlend[0], ComputeRendererShaders::DepthBlend, {"DepthBlend", "ZBuffer"}); + CompileShader(ShaderDepthBlend[1], ComputeRendererShaders::DepthBlend, {"DepthBlend", "WBuffer"}); + CompileShader(ShaderRasteriseNoTexture[0], ComputeRendererShaders::Rasterise, {"Rasterise", "ZBuffer", "NoTexture"}); + CompileShader(ShaderRasteriseNoTexture[1], ComputeRendererShaders::Rasterise, {"Rasterise", "WBuffer", "NoTexture"}); + CompileShader(ShaderRasteriseNoTextureToon[0], ComputeRendererShaders::Rasterise, {"Rasterise", "ZBuffer", "NoTexture", "Toon"}); + CompileShader(ShaderRasteriseNoTextureToon[1], ComputeRendererShaders::Rasterise, {"Rasterise", "WBuffer", "NoTexture", "Toon"}); + CompileShader(ShaderRasteriseNoTextureHighlight[0], ComputeRendererShaders::Rasterise, {"Rasterise", "ZBuffer", "NoTexture", "Highlight"}); + CompileShader(ShaderRasteriseNoTextureHighlight[1], ComputeRendererShaders::Rasterise, {"Rasterise", "WBuffer", "NoTexture", "Highlight"}); + CompileShader(ShaderRasteriseUseTextureDecal[0], ComputeRendererShaders::Rasterise, {"Rasterise", "ZBuffer", "UseTexture", "Decal"}); + CompileShader(ShaderRasteriseUseTextureDecal[1], ComputeRendererShaders::Rasterise, {"Rasterise", "WBuffer", "UseTexture", "Decal"}); + CompileShader(ShaderRasteriseUseTextureModulate[0], ComputeRendererShaders::Rasterise, {"Rasterise", "ZBuffer", "UseTexture", "Modulate"}); + CompileShader(ShaderRasteriseUseTextureModulate[1], ComputeRendererShaders::Rasterise, {"Rasterise", "WBuffer", "UseTexture", "Modulate"}); + CompileShader(ShaderRasteriseUseTextureToon[0], ComputeRendererShaders::Rasterise, {"Rasterise", "ZBuffer", "UseTexture", "Toon"}); + CompileShader(ShaderRasteriseUseTextureToon[1], ComputeRendererShaders::Rasterise, {"Rasterise", "WBuffer", "UseTexture", "Toon"}); + CompileShader(ShaderRasteriseUseTextureHighlight[0], ComputeRendererShaders::Rasterise, {"Rasterise", "ZBuffer", "UseTexture", "Highlight"}); + CompileShader(ShaderRasteriseUseTextureHighlight[1], ComputeRendererShaders::Rasterise, {"Rasterise", "WBuffer", "UseTexture", "Highlight"}); + CompileShader(ShaderRasteriseShadowMask[0], ComputeRendererShaders::Rasterise, {"Rasterise", "ZBuffer", "ShadowMask"}); + CompileShader(ShaderRasteriseShadowMask[1], ComputeRendererShaders::Rasterise, {"Rasterise", "WBuffer", "ShadowMask"}); + CompileShader(ShaderClearCoarseBinMask, ComputeRendererShaders::ClearCoarseBinMask, {"ClearCoarseBinMask"}); + CompileShader(ShaderClearIndirectWorkCount, ComputeRendererShaders::ClearIndirectWorkCount, {"ClearIndirectWorkCount"}); + CompileShader(ShaderCalculateWorkListOffset, ComputeRendererShaders::CalcOffsets, {"CalculateWorkOffsets"}); + CompileShader(ShaderSortWork, ComputeRendererShaders::SortWork, {"SortWork"}); + CompileShader(ShaderFinalPass[0], ComputeRendererShaders::FinalPass, {"FinalPass"}); + CompileShader(ShaderFinalPass[1], ComputeRendererShaders::FinalPass, {"FinalPass", "EdgeMarking"}); + CompileShader(ShaderFinalPass[2], ComputeRendererShaders::FinalPass, {"FinalPass", "Fog"}); + CompileShader(ShaderFinalPass[3], ComputeRendererShaders::FinalPass, {"FinalPass", "EdgeMarking", "Fog"}); + CompileShader(ShaderFinalPass[4], ComputeRendererShaders::FinalPass, {"FinalPass", "AntiAliasing"}); + CompileShader(ShaderFinalPass[5], ComputeRendererShaders::FinalPass, {"FinalPass", "AntiAliasing", "EdgeMarking"}); + CompileShader(ShaderFinalPass[6], ComputeRendererShaders::FinalPass, {"FinalPass", "AntiAliasing", "Fog"}); + CompileShader(ShaderFinalPass[7], ComputeRendererShaders::FinalPass, {"FinalPass", "AntiAliasing", "EdgeMarking", "Fog"}); +} + +void ComputeRenderer::VCount144() +{ + +} + +void ComputeRenderer::SetupAttrs(SpanSetupY* span, Polygon* poly, int from, int to) +{ + span->Z0 = poly->FinalZ[from]; + span->W0 = poly->FinalW[from]; + span->Z1 = poly->FinalZ[to]; + span->W1 = poly->FinalW[to]; + span->ColorR0 = poly->Vertices[from]->FinalColor[0]; + span->ColorG0 = poly->Vertices[from]->FinalColor[1]; + span->ColorB0 = poly->Vertices[from]->FinalColor[2]; + span->ColorR1 = poly->Vertices[to]->FinalColor[0]; + span->ColorG1 = poly->Vertices[to]->FinalColor[1]; + span->ColorB1 = poly->Vertices[to]->FinalColor[2]; + span->TexcoordU0 = poly->Vertices[from]->TexCoords[0]; + span->TexcoordV0 = poly->Vertices[from]->TexCoords[1]; + span->TexcoordU1 = poly->Vertices[to]->TexCoords[0]; + span->TexcoordV1 = poly->Vertices[to]->TexCoords[1]; +} + +void ComputeRenderer::SetupYSpanDummy(RenderPolygon* rp, SpanSetupY* span, Polygon* poly, int vertex, int side, s32 positions[10][2]) +{ + s32 x0 = positions[vertex][0]; + if (side) + { + span->DxInitial = -0x40000; + x0--; + } + else + { + span->DxInitial = 0; + } + + span->X0 = span->X1 = x0; + span->XMin = x0; + span->XMax = x0; + span->Y0 = span->Y1 = positions[vertex][1]; + + if (span->XMin < rp->XMin) + { + rp->XMin = span->XMin; + rp->XMinY = span->Y0; + } + if (span->XMax > rp->XMax) + { + rp->XMax = span->XMax; + rp->XMaxY = span->Y0; + } + + span->Increment = 0; + + span->I0 = span->I1 = span->IRecip = 0; + span->Linear = true; + + span->XCovIncr = 0; + + span->IsDummy = true; + + SetupAttrs(span, poly, vertex, vertex); +} + +void ComputeRenderer::SetupYSpan(RenderPolygon* rp, SpanSetupY* span, Polygon* poly, int from, int to, int side, s32 positions[10][2]) +{ + span->X0 = positions[from][0]; + span->X1 = positions[to][0]; + span->Y0 = positions[from][1]; + span->Y1 = positions[to][1]; + + SetupAttrs(span, poly, from, to); + + s32 minXY, maxXY; + bool negative = false; + if (span->X1 > span->X0) + { + span->XMin = span->X0; + span->XMax = span->X1-1; + + minXY = span->Y0; + maxXY = span->Y1; + } + else if (span->X1 < span->X0) + { + span->XMin = span->X1; + span->XMax = span->X0-1; + negative = true; + + minXY = span->Y1; + maxXY = span->Y0; + } + else + { + span->XMin = span->X0; + if (side) span->XMin--; + span->XMax = span->XMin; + + // doesn't matter for completely vertical slope + minXY = span->Y0; + maxXY = span->Y0; + } + + if (span->XMin < rp->XMin) + { + rp->XMin = span->XMin; + rp->XMinY = minXY; + } + if (span->XMax > rp->XMax) + { + rp->XMax = span->XMax; + rp->XMaxY = maxXY; + } + + span->IsDummy = false; + + s32 xlen = span->XMax+1 - span->XMin; + s32 ylen = span->Y1 - span->Y0; + + // slope increment has a 18-bit fractional part + // note: for some reason, x/y isn't calculated directly, + // instead, 1/y is calculated and then multiplied by x + // TODO: this is still not perfect (see for example x=169 y=33) + if (ylen == 0) + { + span->Increment = 0; + } + else if (ylen == xlen) + { + span->Increment = 0x40000; + } + else + { + s32 yrecip = (1<<18) / ylen; + span->Increment = (span->X1-span->X0) * yrecip; + if (span->Increment < 0) span->Increment = -span->Increment; + } + + bool xMajor = (span->Increment > 0x40000); + + if (side) + { + // right + + if (xMajor) + span->DxInitial = negative ? (0x20000 + 0x40000) : (span->Increment - 0x20000); + else if (span->Increment != 0) + span->DxInitial = negative ? 0x40000 : 0; + else + span->DxInitial = -0x40000; + } + else + { + // left + + if (xMajor) + span->DxInitial = negative ? ((span->Increment - 0x20000) + 0x40000) : 0x20000; + else if (span->Increment != 0) + span->DxInitial = negative ? 0x40000 : 0; + else + span->DxInitial = 0; + } + + if (xMajor) + { + if (side) + { + span->I0 = span->X0 - 1; + span->I1 = span->X1 - 1; + } + else + { + span->I0 = span->X0; + span->I1 = span->X1; + } + + // used for calculating AA coverage + span->XCovIncr = (ylen << 10) / xlen; + } + else + { + span->I0 = span->Y0; + span->I1 = span->Y1; + } + + if (span->I0 != span->I1) + span->IRecip = (1<<30) / (span->I1 - span->I0); + else + span->IRecip = 0; + + span->Linear = (span->W0 == span->W1) && !(span->W0 & 0x7E) && !(span->W1 & 0x7E); + + if ((span->W0 & 0x1) && !(span->W1 & 0x1)) + { + span->W0n = (span->W0 - 1) >> 1; + span->W0d = (span->W0 + 1) >> 1; + span->W1d = span->W1 >> 1; + } + else + { + span->W0n = span->W0 >> 1; + span->W0d = span->W0 >> 1; + span->W1d = span->W1 >> 1; + } +} + +struct Variant +{ + GLuint Texture, Sampler; + u16 Width, Height; + u8 BlendMode; + + bool operator==(const Variant& other) + { + return Texture == other.Texture && Sampler == other.Sampler && BlendMode == other.BlendMode; + } +}; + +/* + Antialiasing + W-Buffer + With Texture + 0 + 1, 3 + 2 + without Texture + 2 + 0, 1, 3 + + => 20 Shader + 1x Shadow Mask +*/ + +void ComputeRenderer::RenderFrame() +{ + //printf("render frame\n"); + + if (!Texcache.Update() && RenderFrameIdentical) + { + return; + } + + int numYSpans = 0; + int numSetupIndices = 0; + + /* + Some games really like to spam small textures, often + to store the data like PPU tiles. E.g. Shantae + or some Mega Man game. Fortunately they are usually kind + enough to not vary the texture size all too often (usually + they just use 8x8 or 16x for everything). + + This is the reason we have this whole mess where textures of + the same size are put into array textures. This allows + to increase the batch size. + Less variance between each Variant hah! + */ + u32 numVariants = 0, prevVariant, prevTexLayer; + Variant variants[MaxVariants]; + + bool enableTextureMaps = RenderDispCnt & (1<<0); + + for (int i = 0; i < RenderNumPolygons; i++) + { + Polygon* polygon = RenderPolygonRAM[i]; + + u32 nverts = polygon->NumVertices; + u32 vtop = polygon->VTop, vbot = polygon->VBottom; + + u32 curVL = vtop, curVR = vtop; + u32 nextVL, nextVR; + + RenderPolygons[i].FirstXSpan = numSetupIndices; + RenderPolygons[i].Attr = polygon->Attr; + + bool foundVariant = false; + if (i > 0) + { + // if the whole texture attribute matches + // the texture layer will also match + Polygon* prevPolygon = RenderPolygonRAM[i - 1]; + foundVariant = prevPolygon->TexParam == polygon->TexParam + && prevPolygon->TexPalette == polygon->TexPalette + && (prevPolygon->Attr & 0x30) == (polygon->Attr & 0x30) + && prevPolygon->IsShadowMask == polygon->IsShadowMask; + } + + if (!foundVariant) + { + Variant variant; + variant.BlendMode = polygon->IsShadowMask ? 4 : ((polygon->Attr >> 4) & 0x3); + variant.Texture = 0; + variant.Sampler = 0; + u32* textureLastVariant = nullptr; + // we always need to look up the texture to get the layer of the array texture + if (enableTextureMaps && (polygon->TexParam >> 26) & 0x7) + { + Texcache.GetTexture(polygon->TexParam, polygon->TexPalette, variant.Texture, prevTexLayer, textureLastVariant); + bool wrapS = (polygon->TexParam >> 16) & 1; + bool wrapT = (polygon->TexParam >> 17) & 1; + bool mirrorS = (polygon->TexParam >> 18) & 1; + bool mirrorT = (polygon->TexParam >> 19) & 1; + variant.Sampler = Samplers[(wrapS ? (mirrorS ? 2 : 1) : 0) + (wrapT ? (mirrorT ? 2 : 1) : 0) * 3]; + + if (*textureLastVariant < numVariants && variants[*textureLastVariant] == variant) + { + foundVariant = true; + prevVariant = *textureLastVariant; + } + } + + if (!foundVariant) + { + for (int j = numVariants - 1; j >= 0; j--) + { + if (variants[j] == variant) + { + foundVariant = true; + prevVariant = j; + goto foundVariant; + } + } + + prevVariant = numVariants; + variants[numVariants] = variant; + variants[numVariants].Width = TextureWidth(polygon->TexParam); + variants[numVariants].Height = TextureHeight(polygon->TexParam); + numVariants++; + assert(numVariants <= MaxVariants); + foundVariant:; + + if (textureLastVariant) + *textureLastVariant = prevVariant; + } + } + RenderPolygons[i].Variant = prevVariant; + RenderPolygons[i].TextureLayer = (float)prevTexLayer; + + if (polygon->FacingView) + { + nextVL = curVL + 1; + if (nextVL >= nverts) nextVL = 0; + nextVR = curVR - 1; + if ((s32)nextVR < 0) nextVR = nverts - 1; + } + else + { + nextVL = curVL - 1; + if ((s32)nextVL < 0) nextVL = nverts - 1; + nextVR = curVR + 1; + if (nextVR >= nverts) nextVR = 0; + } + + s32 scaledPositions[10][2]; + s32 ytop = ScreenHeight, ybot = 0; + for (int i = 0; i < polygon->NumVertices; i++) + { + if (HiresCoordinates) + { + scaledPositions[i][0] = (polygon->Vertices[i]->HiresPosition[0] * ScaleFactor) >> 4; + scaledPositions[i][1] = (polygon->Vertices[i]->HiresPosition[1] * ScaleFactor) >> 4; + } + else + { + scaledPositions[i][0] = polygon->Vertices[i]->FinalPosition[0] * ScaleFactor; + scaledPositions[i][1] = polygon->Vertices[i]->FinalPosition[1] * ScaleFactor; + } + ytop = std::min(scaledPositions[i][1], ytop); + ybot = std::max(scaledPositions[i][1], ybot); + } + RenderPolygons[i].YTop = ytop; + RenderPolygons[i].YBot = ybot; + RenderPolygons[i].XMin = ScreenWidth; + RenderPolygons[i].XMax = 0; + + if (ybot == ytop) + { + vtop = 0; vbot = 0; + + RenderPolygons[i].YBot++; + + int j = 1; + if (scaledPositions[j][0] < scaledPositions[vtop][0]) vtop = j; + if (scaledPositions[j][0] > scaledPositions[vbot][0]) vbot = j; + + j = nverts - 1; + if (scaledPositions[j][0] < scaledPositions[vtop][0]) vtop = j; + if (scaledPositions[j][0] > scaledPositions[vbot][0]) vbot = j; + + assert(numYSpans < MaxYSpanSetups); + u32 curSpanL = numYSpans; + SetupYSpanDummy(&RenderPolygons[i], &YSpanSetups[numYSpans++], polygon, vtop, 0, scaledPositions); + assert(numYSpans < MaxYSpanSetups); + u32 curSpanR = numYSpans; + SetupYSpanDummy(&RenderPolygons[i], &YSpanSetups[numYSpans++], polygon, vbot, 1, scaledPositions); + + YSpanIndices[numSetupIndices].PolyIdx = i; + YSpanIndices[numSetupIndices].SpanIdxL = curSpanL; + YSpanIndices[numSetupIndices].SpanIdxR = curSpanR; + YSpanIndices[numSetupIndices].Y = ytop; + numSetupIndices++; + } + else + { + u32 curSpanL = numYSpans; + assert(numYSpans < MaxYSpanSetups); + SetupYSpan(&RenderPolygons[i], &YSpanSetups[numYSpans++], polygon, curVL, nextVL, 0, scaledPositions); + u32 curSpanR = numYSpans; + assert(numYSpans < MaxYSpanSetups); + SetupYSpan(&RenderPolygons[i], &YSpanSetups[numYSpans++], polygon, curVR, nextVR, 1, scaledPositions); + + for (u32 y = ytop; y < ybot; y++) + { + if (y >= scaledPositions[nextVL][1] && curVL != polygon->VBottom) + { + while (y >= scaledPositions[nextVL][1] && curVL != polygon->VBottom) + { + curVL = nextVL; + if (polygon->FacingView) + { + nextVL = curVL + 1; + if (nextVL >= nverts) + nextVL = 0; + } + else + { + nextVL = curVL - 1; + if ((s32)nextVL < 0) + nextVL = nverts - 1; + } + } + + + assert(numYSpans < MaxYSpanSetups); + curSpanL = numYSpans; + SetupYSpan(&RenderPolygons[i], &YSpanSetups[numYSpans++], polygon, curVL, nextVL, 0, scaledPositions); + } + if (y >= scaledPositions[nextVR][1] && curVR != polygon->VBottom) + { + while (y >= scaledPositions[nextVR][1] && curVR != polygon->VBottom) + { + curVR = nextVR; + if (polygon->FacingView) + { + nextVR = curVR - 1; + if ((s32)nextVR < 0) + nextVR = nverts - 1; + } + else + { + nextVR = curVR + 1; + if (nextVR >= nverts) + nextVR = 0; + } + } + + assert(numYSpans < MaxYSpanSetups); + curSpanR = numYSpans; + SetupYSpan(&RenderPolygons[i] ,&YSpanSetups[numYSpans++], polygon, curVR, nextVR, 1, scaledPositions); + } + + YSpanIndices[numSetupIndices].PolyIdx = i; + YSpanIndices[numSetupIndices].SpanIdxL = curSpanL; + YSpanIndices[numSetupIndices].SpanIdxR = curSpanR; + YSpanIndices[numSetupIndices].Y = y; + numSetupIndices++; + } + } + + //printf("polygon min max %d %d | %d %d\n", RenderPolygons[i].XMin, RenderPolygons[i].XMinY, RenderPolygons[i].XMax, RenderPolygons[i].XMaxY); + } + + /*for (u32 i = 0; i < RenderNumPolygons; i++) + { + if (RenderPolygons[i].Variant >= numVariants) + { + printf("blarb2 %d %d %d\n", RenderPolygons[i].Variant, i, RenderNumPolygons); + } + //assert(RenderPolygons[i].Variant < numVariants); + }*/ + + if (numYSpans > 0) + { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, YSpanSetupMemory); + glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(SpanSetupY)*numYSpans, YSpanSetups); + + glBindBuffer(GL_TEXTURE_BUFFER, YSpanIndicesTextureMemory); + glBufferSubData(GL_TEXTURE_BUFFER, 0, numSetupIndices*4*2, YSpanIndices.data()); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, RenderPolygonMemory); + glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, RenderNumPolygons*sizeof(RenderPolygon), RenderPolygons); + // we haven't accessed image data yet, so we don't need to invalidate anything + } + + //printf("found via %d %d %d of %d\n", foundviatexcache, foundviaprev, numslow, RenderNumPolygons); + + // bind everything + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, RenderPolygonMemory); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, XSpanSetupMemory); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, YSpanSetupMemory); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, FinalTileMemory); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, BinResultMemory); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, WorkDescMemory); + + MetaUniform meta; + meta.DispCnt = RenderDispCnt; + meta.NumPolygons = RenderNumPolygons; + meta.NumVariants = numVariants; + meta.AlphaRef = RenderAlphaRef; + { + u32 r = (RenderClearAttr1 << 1) & 0x3E; if (r) r++; + u32 g = (RenderClearAttr1 >> 4) & 0x3E; if (g) g++; + u32 b = (RenderClearAttr1 >> 9) & 0x3E; if (b) b++; + u32 a = (RenderClearAttr1 >> 16) & 0x1F; + meta.ClearColor = r | (g << 8) | (b << 16) | (a << 24); + meta.ClearDepth = ((RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; + meta.ClearAttr = RenderClearAttr1 & 0x3F008000; + } + for (u32 i = 0; i < 32; i++) + { + u32 color = RenderToonTable[i]; + u32 r = (color << 1) & 0x3E; + u32 g = (color >> 4) & 0x3E; + u32 b = (color >> 9) & 0x3E; + if (r) r++; + if (g) g++; + if (b) b++; + + meta.ToonTable[i*4+0] = r | (g << 8) | (b << 16); + } + for (u32 i = 0; i < 34; i++) + { + meta.ToonTable[i*4+1] = RenderFogDensityTable[i]; + } + for (u32 i = 0; i < 8; i++) + { + u32 color = RenderEdgeTable[i]; + u32 r = (color << 1) & 0x3E; + u32 g = (color >> 4) & 0x3E; + u32 b = (color >> 9) & 0x3E; + if (r) r++; + if (g) g++; + if (b) b++; + + meta.ToonTable[i*4+2] = r | (g << 8) | (b << 16); + } + meta.FogOffset = RenderFogOffset; + meta.FogShift = RenderFogShift; + { + u32 fogR = (RenderFogColor << 1) & 0x3E; if (fogR) fogR++; + u32 fogG = (RenderFogColor >> 4) & 0x3E; if (fogG) fogG++; + u32 fogB = (RenderFogColor >> 9) & 0x3E; if (fogB) fogB++; + u32 fogA = (RenderFogColor >> 16) & 0x1F; + meta.FogColor = fogR | (fogG << 8) | (fogB << 16) | (fogA << 24); + } + + glBindBuffer(GL_UNIFORM_BUFFER, MetaUniformMemory); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(MetaUniform), &meta); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, MetaUniformMemory); + + glUseProgram(ShaderClearCoarseBinMask); + glDispatchCompute(TilesPerLine*TileLines/32, 1, 1); + + bool wbuffer = false; + if (numYSpans > 0) + { + wbuffer = RenderPolygonRAM[0]->WBuffer; + + glUseProgram(ShaderClearIndirectWorkCount); + glDispatchCompute((numVariants+31)/32, 1, 1); + + // calculate x-spans + glBindImageTexture(0, YSpanIndicesTexture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA16UI); + glUseProgram(ShaderInterpXSpans[wbuffer]); + glDispatchCompute((numSetupIndices + 31) / 32, 1, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BUFFER); + + // bin polygons + glUseProgram(ShaderBinCombined); + glDispatchCompute(((RenderNumPolygons + 31) / 32), ScreenWidth/CoarseTileW, ScreenHeight/CoarseTileH); + glMemoryBarrier(GL_SHADER_STORAGE_BUFFER); + + // calculate list offsets + glUseProgram(ShaderCalculateWorkListOffset); + glDispatchCompute((numVariants + 31) / 32, 1, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BUFFER); + + // sort shader work + glUseProgram(ShaderSortWork); + glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, BinResultMemory); + glDispatchComputeIndirect(offsetof(BinResultHeader, SortWorkWorkCount)); + glMemoryBarrier(GL_SHADER_STORAGE_BUFFER); + + glActiveTexture(GL_TEXTURE0); + + for (int i = 0; i < tilememoryLayer_Num; i++) + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2+i, TileMemory[i]); + + // rasterise + { + bool highLightMode = RenderDispCnt & (1<<1); + + GLuint shadersNoTexture[] = + { + ShaderRasteriseNoTexture[wbuffer], + ShaderRasteriseNoTexture[wbuffer], + highLightMode + ? ShaderRasteriseNoTextureHighlight[wbuffer] + : ShaderRasteriseNoTextureToon[wbuffer], + ShaderRasteriseNoTexture[wbuffer], + ShaderRasteriseShadowMask[wbuffer] + }; + GLuint shadersUseTexture[] = + { + ShaderRasteriseUseTextureModulate[wbuffer], + ShaderRasteriseUseTextureDecal[wbuffer], + highLightMode + ? ShaderRasteriseUseTextureHighlight[wbuffer] + : ShaderRasteriseUseTextureToon[wbuffer], + ShaderRasteriseUseTextureDecal[wbuffer], + ShaderRasteriseShadowMask[wbuffer] + }; + + GLuint prevShader = 0; + s32 prevTexture = 0, prevSampler = 0; + for (int i = 0; i < numVariants; i++) + { + GLuint shader = 0; + if (variants[i].Texture == 0) + { + shader = shadersNoTexture[variants[i].BlendMode]; + } + else + { + shader = shadersUseTexture[variants[i].BlendMode]; + if (variants[i].Texture != prevTexture) + { + glBindTexture(GL_TEXTURE_2D_ARRAY, variants[i].Texture); + prevTexture = variants[i].Texture; + } + if (variants[i].Sampler != prevSampler) + { + glBindSampler(0, variants[i].Sampler); + prevSampler = variants[i].Sampler; + } + } + assert(shader != 0); + if (shader != prevShader) + { + glUseProgram(shader); + prevShader = shader; + } + + glUniform1ui(UniformIdxCurVariant, i); + glUniform2f(UniformIdxTextureSize, 1.f / variants[i].Width, 1.f / variants[i].Height); + glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, BinResultMemory); + glDispatchComputeIndirect(offsetof(BinResultHeader, VariantWorkCount) + i*4*4); + } + } + } + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + + // compose final image + glUseProgram(ShaderDepthBlend[wbuffer]); + glDispatchCompute(ScreenWidth/TileSize, ScreenHeight/TileSize, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + + glBindImageTexture(0, Framebuffer, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); + glBindImageTexture(1, LowResFramebuffer, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8UI); + u32 finalPassShader = 0; + if (RenderDispCnt & (1<<4)) + finalPassShader |= 0x4; + if (RenderDispCnt & (1<<7)) + finalPassShader |= 0x2; + if (RenderDispCnt & (1<<5)) + finalPassShader |= 0x1; + + glUseProgram(ShaderFinalPass[finalPassShader]); + glDispatchCompute(ScreenWidth/32, ScreenHeight, 1); + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + glBindSampler(0, 0); + + /*u64 starttime = armGetSystemTick(); + EmuQueue.waitIdle(); + printf("total time %f\n", armTicksToNs(armGetSystemTick()-starttime)*0.000001f);*/ + + /*for (u32 i = 0; i < RenderNumPolygons; i++) + { + if (RenderPolygons[i].Variant >= numVariants) + { + printf("blarb %d %d %d\n", RenderPolygons[i].Variant, i, RenderNumPolygons); + } + //assert(RenderPolygons[i].Variant < numVariants); + }*/ + + /*for (int i = 0; i < binresult->SortWorkWorkCount[0]*32; i++) + { + printf("sorted %x %x\n", binresult->SortedWork[i*2+0], binresult->SortedWork[i*2+1]); + }*/ +/* if (polygonvisible != -1) + { + SpanSetupX* xspans = Gfx::DataHeap->CpuAddr<SpanSetupX>(XSpanSetupMemory); + printf("span result\n"); + Polygon* poly = RenderPolygonRAM[polygonvisible]; + u32 xspanoffset = RenderPolygons[polygonvisible].FirstXSpan; + for (u32 i = 0; i < (poly->YBottom - poly->YTop); i++) + { + printf("%d: %d - %d | %d %d | %d %d\n", i + poly->YTop, xspans[xspanoffset + i].X0, xspans[xspanoffset + i].X1, xspans[xspanoffset + i].__pad0, xspans[xspanoffset + i].__pad1, RenderPolygons[polygonvisible].YTop, RenderPolygons[polygonvisible].YBot); + } + }*/ +/* + printf("xspans: %d\n", numSetupIndices); + SpanSetupX* xspans = Gfx::DataHeap->CpuAddr<SpanSetupX>(XSpanSetupMemory[curSlice]); + for (int i = 0; i < numSetupIndices; i++) + { + printf("poly %d %d %d | line %d | %d to %d\n", YSpanIndices[i].PolyIdx, YSpanIndices[i].SpanIdxL, YSpanIndices[i].SpanIdxR, YSpanIndices[i].Y, xspans[i].X0, xspans[i].X1); + } + printf("bin result\n"); + BinResult* binresult = Gfx::DataHeap->CpuAddr<BinResult>(BinResultMemory); + for (u32 y = 0; y < 192/8; y++) + { + for (u32 x = 0; x < 256/8; x++) + { + printf("%08x ", binresult->BinnedMaskCoarse[(x + y * (256/8)) * 2]); + } + printf("\n"); + }*/ +} + +void ComputeRenderer::RestartFrame() +{ + +} + +u32* ComputeRenderer::GetLine(int line) +{ + int stride = 256; + + if (line == 0) + { + glBindBuffer(GL_PIXEL_PACK_BUFFER, PixelBuffer); + u8* data = (u8*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); + if (data) memcpy(&FramebufferCPU[0], data, 4*stride*192); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + } + + return &FramebufferCPU[stride * line]; +} + +void ComputeRenderer::SetupAccelFrame() +{ + glBindTexture(GL_TEXTURE_2D, Framebuffer); +} + +void ComputeRenderer::PrepareCaptureFrame() +{ + glBindBuffer(GL_PIXEL_PACK_BUFFER, PixelBuffer); + glBindTexture(GL_TEXTURE_2D, LowResFramebuffer); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, nullptr); +} + +} \ No newline at end of file diff --git a/src/GPU3D_Compute.h b/src/GPU3D_Compute.h new file mode 100644 index 0000000..b36bc18 --- /dev/null +++ b/src/GPU3D_Compute.h @@ -0,0 +1,227 @@ +/* + Copyright 2016-2022 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef GPU3D_COMPUTE +#define GPU3D_COMPUTE + +#include <memory> + +#include "GPU3D.h" + +#include "OpenGLSupport.h" + +#include "GPU3D_TexcacheOpenGL.h" + +#include "NonStupidBitfield.h" + +namespace GPU3D +{ + +class ComputeRenderer : public Renderer3D +{ +public: + static std::unique_ptr<ComputeRenderer> New(); + ~ComputeRenderer() override; + + void Reset() override; + + void SetRenderSettings(GPU::RenderSettings& settings) override; + + void VCount144() override; + + void RenderFrame() override; + void RestartFrame() override; + u32* GetLine(int line) override; + + void SetupAccelFrame() override; + void PrepareCaptureFrame() override; +private: + ComputeRenderer(); + + GLuint ShaderInterpXSpans[2]; + GLuint ShaderBinCombined; + GLuint ShaderDepthBlend[2]; + GLuint ShaderRasteriseNoTexture[2]; + GLuint ShaderRasteriseNoTextureToon[2]; + GLuint ShaderRasteriseNoTextureHighlight[2]; + GLuint ShaderRasteriseUseTextureDecal[2]; + GLuint ShaderRasteriseUseTextureModulate[2]; + GLuint ShaderRasteriseUseTextureToon[2]; + GLuint ShaderRasteriseUseTextureHighlight[2]; + GLuint ShaderRasteriseShadowMask[2]; + GLuint ShaderClearCoarseBinMask; + GLuint ShaderClearIndirectWorkCount; + GLuint ShaderCalculateWorkListOffset; + GLuint ShaderSortWork; + GLuint ShaderFinalPass[8]; + + GLuint YSpanIndicesTextureMemory; + GLuint YSpanIndicesTexture; + GLuint YSpanSetupMemory; + GLuint XSpanSetupMemory; + GLuint BinResultMemory; + GLuint RenderPolygonMemory; + GLuint WorkDescMemory; + + enum + { + tilememoryLayer_Color, + tilememoryLayer_Depth, + tilememoryLayer_Attr, + tilememoryLayer_Num, + }; + + GLuint TileMemory[tilememoryLayer_Num]; + GLuint FinalTileMemory; + + u32 DummyLine[256] = {}; + + struct SpanSetupY + { + // Attributes + s32 Z0, Z1, W0, W1; + s32 ColorR0, ColorG0, ColorB0; + s32 ColorR1, ColorG1, ColorB1; + s32 TexcoordU0, TexcoordV0; + s32 TexcoordU1, TexcoordV1; + + // Interpolator + s32 I0, I1; + s32 Linear; + s32 IRecip; + s32 W0n, W0d, W1d; + + // Slope + s32 Increment; + + s32 X0, X1, Y0, Y1; + s32 XMin, XMax; + s32 DxInitial; + + s32 XCovIncr; + u32 IsDummy; + }; + struct SpanSetupX + { + s32 X0, X1; + + s32 EdgeLenL, EdgeLenR, EdgeCovL, EdgeCovR; + + s32 XRecip; + + u32 Flags; + + s32 Z0, Z1, W0, W1; + s32 ColorR0, ColorG0, ColorB0; + s32 ColorR1, ColorG1, ColorB1; + s32 TexcoordU0, TexcoordV0; + s32 TexcoordU1, TexcoordV1; + + s32 CovLInitial, CovRInitial; + }; + struct SetupIndices + { + u16 PolyIdx, SpanIdxL, SpanIdxR, Y; + }; + struct RenderPolygon + { + u32 FirstXSpan; + s32 YTop, YBot; + + s32 XMin, XMax; + s32 XMinY, XMaxY; + + u32 Variant; + u32 Attr; + + float TextureLayer; + }; + + static constexpr int TileSize = 8; + static constexpr int CoarseTileCountX = 8; + static constexpr int CoarseTileCountY = 4; + static constexpr int CoarseTileW = CoarseTileCountX * TileSize; + static constexpr int CoarseTileH = CoarseTileCountY * TileSize; + + static constexpr int BinStride = 2048/32; + static constexpr int CoarseBinStride = BinStride/32; + + static constexpr int MaxVariants = 256; + + static constexpr int UniformIdxCurVariant = 0; + static constexpr int UniformIdxTextureSize = 1; + + static constexpr int MaxFullscreenLayers = 16; + + struct BinResultHeader + { + u32 VariantWorkCount[MaxVariants*4]; + u32 SortedWorkOffset[MaxVariants]; + + u32 SortWorkWorkCount[4]; + }; + + static const int MaxYSpanSetups = 6144*2; + std::vector<SetupIndices> YSpanIndices; + SpanSetupY YSpanSetups[MaxYSpanSetups]; + RenderPolygon RenderPolygons[2048]; + + TexcacheOpenGL Texcache; + + struct MetaUniform + { + u32 NumPolygons; + u32 NumVariants; + + u32 AlphaRef; + u32 DispCnt; + + u32 ToonTable[4*34]; + + u32 ClearColor, ClearDepth, ClearAttr; + + u32 FogOffset, FogShift, FogColor; + }; + GLuint MetaUniformMemory; + + GLuint Samplers[9]; + + GLuint Framebuffer = 0; + GLuint LowResFramebuffer; + GLuint PixelBuffer; + + u32 FramebufferCPU[256*192]; + + int ScreenWidth, ScreenHeight; + int TilesPerLine, TileLines; + int ScaleFactor = -1; + int MaxWorkTiles; + bool HiresCoordinates; + + void DeleteShaders(); + + void SetupAttrs(SpanSetupY* span, Polygon* poly, int from, int to); + void SetupYSpan(RenderPolygon* rp, SpanSetupY* span, Polygon* poly, int from, int to, int side, s32 positions[10][2]); + void SetupYSpanDummy(RenderPolygon* rp, SpanSetupY* span, Polygon* poly, int vertex, int side, s32 positions[10][2]); + + bool CompileShader(GLuint& shader, const std::string& source, const std::initializer_list<const char*>& defines); +}; + +} + +#endif \ No newline at end of file diff --git a/src/GPU3D_Compute_shaders.h b/src/GPU3D_Compute_shaders.h new file mode 100644 index 0000000..39858a4 --- /dev/null +++ b/src/GPU3D_Compute_shaders.h @@ -0,0 +1,1665 @@ +/* + Copyright 2016-2022 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef GPU3D_COMPUTE_SHADERS +#define GPU3D_COMPUTE_SHADERS + +#include <string> + +namespace GPU3D +{ + +namespace ComputeRendererShaders +{ + +// defines: +// InterpSpans +// BinCombined +// Rasterise +// DepthBlend +// ClearCoarseBinMask +// ClearIndirectWorkCount +// CalculateWorkOffsets +// SortWork +// FinalPass + +// AntiAliasing +// EdgeMarking +// Fog + +// ZBuffer +// WBuffer + +// for Rasterise +// NoTexture +// UseTexture +// Decal +// Modulate +// Toon +// Highlight +// ShadowMask + + +/* + Some notes on signed division: + + we want to avoid it, so we can avoid higher precision numbers + in a few places. + + Fortunately all divisions *should* assuming I'm not mistaken + have the same sign on the divisor and the dividend. + + Thus we apply: + + assuming n < 0 <=> d < 0 + n/d = abs(n)/abs(d) + +*/ + +const std::string XSpanSetupBuffer{R"( + +const uint XSpanSetup_Linear = 1U << 0; +const uint XSpanSetup_FillInside = 1U << 1; +const uint XSpanSetup_FillLeft = 1U << 2; +const uint XSpanSetup_FillRight = 1U << 3; + +struct XSpanSetup +{ + int X0, X1; + + int InsideStart, InsideEnd, EdgeCovL, EdgeCovR; + + int XRecip; + + uint Flags; + + int Z0, Z1, W0, W1; + int ColorR0, ColorG0, ColorB0; + int ColorR1, ColorG1, ColorB1; + int TexcoordU0, TexcoordV0; + int TexcoordU1, TexcoordV1; + + int CovLInitial, CovRInitial; +}; + +#if defined(Rasterise) +int CalcYFactorX(XSpanSetup span, int x) +{ + x -= span.X0; + + if (span.X0 != span.X1) + { + uint numLo = uint(x) * uint(span.W0); + uint numHi = 0U; + numHi |= numLo >> (32U-YFactorShift); + numLo <<= YFactorShift; + + uint den = uint(x) * uint(span.W0) + uint(span.X1 - span.X0 - x) * uint(span.W1); + + if (den == 0) + return 0; + else + return int(Div64_32_32(numHi, numLo, den)); + } + else + { + return 0; + } +} +#endif + +layout (std430, binding = 1) buffer XSpanSetupsBuffer +{ + XSpanSetup XSpanSetups[]; +}; + +)"}; + +const std::string YSpanSetupBuffer{R"( + +struct YSpanSetup +{ + // Attributes + int Z0, Z1, W0, W1; + int ColorR0, ColorG0, ColorB0; + int ColorR1, ColorG1, ColorB1; + int TexcoordU0, TexcoordV0; + int TexcoordU1, TexcoordV1; + + // Interpolator + int I0, I1; + bool Linear; + int IRecip; + int W0n, W0d, W1d; + + // Slope + int Increment; + + int X0, X1, Y0, Y1; + int XMin, XMax; + int DxInitial; + + int XCovIncr; + + bool IsDummy; +}; + +#if defined(InterpSpans) +int CalcYFactorY(YSpanSetup span, int i) +{ + /* + maybe it would be better to do use a 32x32=64 multiplication? + */ + uint numLo = uint(abs(i)) * uint(span.W0n); + uint numHi = 0U; + numHi |= numLo >> (32U-YFactorShift); + numLo <<= YFactorShift; + + uint den = uint(abs(i)) * uint(span.W0d) + uint(abs(span.I1 - span.I0 - i)) * span.W1d; + + if (den == 0) + { + return 0; + } + else + { + return int(Div64_32_32(numHi, numLo, den)); + } +} + +int CalculateDx(int y, YSpanSetup span) +{ + return span.DxInitial + (y - span.Y0) * span.Increment; +} + +int CalculateX(int dx, YSpanSetup span) +{ + int x = span.X0; + if (span.X1 < span.X0) + x -= dx >> 18; + else + x += dx >> 18; + return clamp(x, span.XMin, span.XMax); +} + +void EdgeParams_XMajor(bool side, int dx, YSpanSetup span, out int edgelen, out int edgecov) +{ + bool negative = span.X1 < span.X0; + int len; + if (side != negative) + len = (dx >> 18) - ((dx-span.Increment) >> 18); + else + len = ((dx+span.Increment) >> 18) - (dx >> 18); + edgelen = len; + + int xlen = span.XMax + 1 - span.XMin; + int startx = dx >> 18; + if (negative) startx = xlen - startx; + if (side) startx = startx - len + 1; + + uint r; + int startcov = int(Div(uint(((startx << 10) + 0x1FF) * (span.Y1 - span.Y0)), uint(xlen), r)); + edgecov = (1<<31) | ((startcov & 0x3FF) << 12) | (span.XCovIncr & 0x3FF); +} + +void EdgeParams_YMajor(bool side, int dx, YSpanSetup span, out int edgelen, out int edgecov) +{ + bool negative = span.X1 < span.X0; + edgelen = 1; + + if (span.Increment == 0) + { + edgecov = 31; + } + else + { + int cov = ((dx >> 9) + (span.Increment >> 10)) >> 4; + if ((cov >> 5) != (dx >> 18)) cov = 31; + cov &= 0x1F; + if (side == negative) cov = 0x1F - cov; + + edgecov = cov; + } +} +#endif + +layout (std430, binding = 2) buffer YSpanSetupsBuffer +{ + YSpanSetup YSpanSetups[]; +}; + +)"}; + +const std::string PolygonBuffer{R"( +struct Polygon +{ + int FirstXSpan; + int YTop, YBot; + + int XMin, XMax; + int XMinY, XMaxY; + + int Variant; + + uint Attr; + + float TextureLayer; +}; + +layout (std430, binding = 0) readonly buffer PolygonBuffer +{ + Polygon Polygons[]; +}; +)"}; + +const std::string BinningBuffer{R"( + +layout (std430, binding = 6) buffer BinResultBuffer +{ + uvec4 VariantWorkCount[MaxVariants]; + uint SortedWorkOffset[MaxVariants]; + + uvec4 SortWorkWorkCount; + + uint BinningMaskAndOffset[]; + //uint BinnedMaskCoarse[TilesPerLine*TileLines*CoarseBinStride]; + //uint BinnedMask[TilesPerLine*TileLines*BinStride]; + //uint WorkOffsets[TilesPerLine*TileLines*BinStride]; +}; + +const int BinningCoarseMaskStart = 0; +const int BinningMaskStart = BinningCoarseMaskStart+TilesPerLine*TileLines*CoarseBinStride; +const int BinningWorkOffsetsStart = BinningMaskStart+TilesPerLine*TileLines*BinStride; + +)"}; + +/* + structure of each WorkDesc item: + x: + bits 0-10: polygon idx + bits 11-31: tile idx (before sorting within variant after sorting within all tiles) + y: + bits 0-15: X position on screen + bits 15-31: Y position on screen +*/ +const std::string WorkDescBuffer{R"( +layout (std430, binding = 7) buffer WorkDescBuffer +{ + //uvec2 UnsortedWorkDescs[MaxWorkTiles]; + //uvec2 SortedWorkDescs[MaxWorkTiles]; + uvec2 WorkDescs[]; +}; + +const uint WorkDescsUnsortedStart = 0; +const uint WorkDescsSortedStart = WorkDescsUnsortedStart+MaxWorkTiles; + +)"}; + +const std::string Tilebuffers{R"( +layout (std430, binding = 2) buffer ColorTileBuffer +{ + uint ColorTiles[]; +}; +layout (std430, binding = 3) buffer DepthTileBuffer +{ + uint DepthTiles[]; +}; +layout (std430, binding = 4) buffer AttrTileBuffer +{ + uint AttrTiles[]; +}; + +)"}; + +const std::string ResultBuffer{R"( +layout (std430, binding = 5) buffer ResultBuffer +{ + uint ResultValue[]; +}; + +const uint ResultColorStart = 0; +const uint ResultDepthStart = ResultColorStart+ScreenWidth*ScreenHeight*2; +const uint ResultAttrStart = ResultDepthStart+ScreenWidth*ScreenHeight*2; +)"}; + +const char* Common = R"( + +#define TileSize 8 +const int CoarseTileCountX = 8; +const int CoarseTileCountY = 4; +const int CoarseTileW = (CoarseTileCountX * TileSize); +const int CoarseTileH = (CoarseTileCountY * TileSize); + +const int FramebufferStride = ScreenWidth*ScreenHeight; +const int TilesPerLine = ScreenWidth/TileSize; +const int TileLines = ScreenHeight/TileSize; + +const int BinStride = 2048/32; +const int CoarseBinStride = BinStride/32; + +const int MaxVariants = 256; + +layout (std140, binding = 0) uniform MetaUniform +{ + uint NumPolygons; + uint NumVariants; + + int AlphaRef; + + uint DispCnt; + + // r = Toon + // g = Fog Density + // b = Edge Color + uvec4 ToonTable[34]; + + uint ClearColor, ClearDepth, ClearAttr; + + uint FogOffset, FogShift, FogColor; +}; + +#ifdef InterpSpans +const int YFactorShift = 9; +#else +const int YFactorShift = 8; +#endif + +#if defined(InterpSpans) || defined(Rasterise) +uint Umulh(uint a, uint b) +{ + uint lo, hi; + umulExtended(a, b, hi, lo); + return hi; +} + +const uint startTable[256] = uint[256]( + 254, 252, 250, 248, 246, 244, 242, 240, 238, 236, 234, 233, 231, 229, 227, 225, 224, 222, 220, 218, 217, 215, 213, 212, 210, 208, 207, 205, 203, 202, 200, 199, 197, 195, 194, 192, 191, 189, 188, 186, 185, 183, 182, 180, 179, 178, 176, 175, 173, 172, 170, 169, 168, 166, 165, 164, 162, 161, 160, 158, +157, 156, 154, 153, 152, 151, 149, 148, 147, 146, 144, 143, 142, 141, 139, 138, 137, 136, 135, 134, 132, 131, 130, 129, 128, 127, 126, 125, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 88, 87, 86, 85, 84, 83, 82, 81, 80, 80, 79, 78, 77, 76, 75, 74, 74, 73, 72, 71, 70, 70, 69, 68, 67, 66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 59, 58, 57, 56, 56, 55, 54, 53, 53, 52, 51, 50, 50, 49, 48, 48, 47, 46, 46, 45, 44, 43, 43, 42, 41, 41, 40, 39, 39, 38, 37, 37, 36, 35, 35, 34, 33, 33, 32, 32, 31, 30, 30, 29, 28, 28, 27, 27, 26, 25, 25, 24, 24, 23, 22, 22, 21, 21, 20, 19, 19, 18, 18, 17, 17, 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0 +); + +uint Div(uint x, uint y, out uint r) +{ + // https://www.microsoft.com/en-us/research/publication/software-integer-division/ + uint k = 31 - findMSB(y); + uint ty = (y << k) >> (32 - 9); + uint t = startTable[ty - 256] + 256; + uint z = (t << (32 - 9)) >> (32 - k - 1); + uint my = 0 - y; + + z += Umulh(z, my * z); + z += Umulh(z, my * z); + + uint q = Umulh(x, z); + r = x - y * q; + if(r >= y) + { + r = r - y; + q = q + 1; + if(r >= y) + { + r = r - y; + q = q + 1; + } + } + + return q; +} + +uint Div64_32_32(uint numHi, uint numLo, uint den) +{ + // based on https://github.com/ridiculousfish/libdivide/blob/3bd34388573681ce563348cdf04fe15d24770d04/libdivide.h#L469 + // modified to work with half the size 64/32=32 instead of 128/64=64 + // for further details see https://ridiculousfish.com/blog/posts/labor-of-division-episode-iv.html + + // We work in base 2**16. + // A uint32 holds a single digit (in the lower 16 bit). A uint32 holds two digits. + // Our numerator is conceptually [num3, num2, num1, num0]. + // Our denominator is [den1, den0]. + const uint b = (1U << 16); + + // Determine the normalization factor. We multiply den by this, so that its leading digit is at + // least half b. In binary this means just shifting left by the number of leading zeros, so that + // there's a 1 in the MSB. + // We also shift numer by the same amount. This cannot overflow because numHi < den. + // The expression (-shift & 63) is the same as (64 - shift), except it avoids the UB of shifting + // by 64. (it's also UB in GLSL!!!!) + uint shift = 31 - findMSB(den); + den <<= shift; + numHi <<= shift; + numHi |= (numLo >> (-shift & 31U)) & uint(-int(shift) >> 31); + numLo <<= shift; + + // Extract the low digits of the numerator and both digits of the denominator. + uint num1 = (numLo >> 16); + uint num0 = (numLo & 0xFFFFU); + uint den1 = (den >> 16); + uint den0 = (den & 0xFFFFU); + + // We wish to compute q1 = [n3 n2 n1] / [d1 d0]. + // Estimate q1 as [n3 n2] / [d1], and then correct it. + // Note while qhat may be 2 digits, q1 is always 1 digit. + + uint rhat; + uint qhat = Div(numHi, den1, rhat); + uint c1 = qhat * den0; + uint c2 = rhat * b + num1; + if (c1 > c2) qhat -= (c1 - c2 > den) ? 2 : 1; + uint q1 = qhat & 0xFFFFU; + + // Compute the true (partial) remainder. + uint rem = numHi * b + num1 - q1 * den; + + // We wish to compute q0 = [rem1 rem0 n0] / [d1 d0]. + // Estimate q0 as [rem1 rem0] / [d1] and correct it. + qhat = Div(rem, den1, rhat); + c1 = qhat * den0; + c2 = rhat * b + num0; + if (c1 > c2) qhat -= (c1 - c2 > den) ? 2 : 1; + + return bitfieldInsert(qhat, q1, 16, 16); +} + +int InterpolateAttrPersp(int y0, int y1, int ifactor) +{ + if (y0 == y1) + return y0; + + if (y0 < y1) + return y0 + (((y1-y0) * ifactor) >> YFactorShift); + else + return y1 + (((y0-y1) * ((1<<YFactorShift)-ifactor)) >> YFactorShift); +} + +int InterpolateAttrLinear(int y0, int y1, int i, int irecip, int idiff) +{ + if (y0 == y1) + return y0; + +#ifndef Rasterise + irecip = abs(irecip); +#endif + + uint mulLo, mulHi, carry; + if (y0 < y1) + { +#ifndef Rasterise + uint offset = uint(abs(i)); +#else + uint offset = uint(i); +#endif + umulExtended(uint(y1-y0)*offset, uint(irecip), mulHi, mulLo); + mulLo = uaddCarry(mulLo, 3U<<24, carry); + mulHi += carry; + return y0 + int((mulLo >> 30) | (mulHi << (32 - 30))); + //return y0 + int(((int64_t(y1-y0) * int64_t(offset) * int64_t(irecip)) + int64_t(3<<24)) >> 30); + } + else + { +#ifndef Rasterise + uint offset = uint(abs(idiff-i)); +#else + uint offset = uint(idiff-i); +#endif + umulExtended(uint(y0-y1)*offset, uint(irecip), mulHi, mulLo); + mulLo = uaddCarry(mulLo, 3<<24, carry); + mulHi += carry; + return y1 + int((mulLo >> 30) | (mulHi << (32 - 30))); + //return y1 + int(((int64_t(y0-y1) * int64_t(offset) * int64_t(irecip)) + int64_t(3<<24)) >> 30); + } +} + +uint InterpolateZZBuffer(int z0, int z1, int i, int irecip, int idiff) +{ + if (z0 == z1) + return z0; + + uint base, disp, factor; + if (z0 < z1) + { + base = uint(z0); + disp = uint(z1 - z0); + factor = uint(abs(i)); + } + else + { + base = uint(z1); + disp = uint(z0 - z1), + factor = uint(abs(idiff - i)); + } + +#ifdef InterpSpans + int shiftl = 0; + const int shiftr = 22; + if (disp > 0x3FF) + { + shiftl = findMSB(disp) - 9; + disp >>= shiftl; + } +#else + disp >>= 9; + const int shiftl = 0; + const int shiftr = 13; +#endif + uint mulLo, mulHi; + + umulExtended(disp * factor, abs(irecip) >> 8, mulHi, mulLo); + + return base + (((mulLo >> shiftr) | (mulHi << (32 - shiftr))) << shiftl); +/* + int base, disp, factor; + if (z0 < z1) + { + base = z0; + disp = z1 - z0; + factor = i; + } + else + { + base = z1; + disp = z0 - z1, + factor = idiff - i; + } + +#ifdef InterpSpans + { + int shift = 0; + while (disp > 0x3FF) + { + disp >>= 1; + shift++; + } + + return base + int(((int64_t(disp) * int64_t(factor) * (int64_t(irecip) >> 8)) >> 22) << shift); + } +#else + { + disp >>= 9; + return base + int((int64_t(disp) * int64_t(factor) * (int64_t(irecip) >> 8)) >> 13); + } +#endif*/ +} + +uint InterpolateZWBuffer(int z0, int z1, int ifactor) +{ + if (z0 == z1) + return z0; + +#ifdef Rasterise + // since the precision along x spans is only 8 bit the result will always fit in 32-bit + if (z0 < z1) + { + return uint(z0) + (((z1-z0) * ifactor) >> YFactorShift); + } + else + { + return uint(z1) + (((z0-z1) * ((1<<YFactorShift)-ifactor)) >> YFactorShift); + } +#else + uint mulLo, mulHi; + if (z0 < z1) + { + umulExtended(z1-z0, ifactor, mulHi, mulLo); + // 64-bit shift + return uint(z0) + ((mulLo >> YFactorShift) | (mulHi << (32-YFactorShift))); + } + else + { + umulExtended(z0-z1, (1<<YFactorShift)-ifactor, mulHi, mulLo); + return uint(z1) + ((mulLo >> YFactorShift) | (mulHi << (32-YFactorShift))); + } +#endif + /*if (z0 < z1) + { + return uint(z0) + uint((int64_t(z1-z0) * int64_t(ifactor)) >> YFactorShift); + } + else + { + return uint(z1) + uint((int64_t(z0-z1) * int64_t((1<<YFactorShift)-ifactor)) >> YFactorShift); + }*/ +} +#endif + +)"; + +const std::string InterpSpans = + PolygonBuffer + + XSpanSetupBuffer + + YSpanSetupBuffer + R"( +layout (local_size_x = 32) in; + +layout (binding = 0, rgba16ui) uniform readonly uimageBuffer SetupIndices; + +void main() +{ + uvec4 setup = imageLoad(SetupIndices, int(gl_GlobalInvocationID.x)); + + YSpanSetup spanL = YSpanSetups[setup.y]; + YSpanSetup spanR = YSpanSetups[setup.z]; + + XSpanSetup xspan; + xspan.Flags = 0U; + + int y = int(setup.w); + + int dxl = CalculateDx(y, spanL); + int dxr = CalculateDx(y, spanR); + + int xl = CalculateX(dxl, spanL); + int xr = CalculateX(dxr, spanR); + + Polygon polygon = Polygons[setup.x]; + + int edgeLenL, edgeLenR; + + if (xl > xr) + { + YSpanSetup tmpSpan = spanL; + spanL = spanR; + spanR = tmpSpan; + + int tmp = xl; + xl = xr; + xr = tmp; + + EdgeParams_YMajor(false, dxr, spanL, edgeLenL, xspan.EdgeCovL); + EdgeParams_YMajor(true, dxl, spanR, edgeLenR, xspan.EdgeCovR); + } + else + { + // edges are the right way + if (spanL.Increment > 0x40000) + EdgeParams_XMajor(false, dxl, spanL, edgeLenL, xspan.EdgeCovL); + else + EdgeParams_YMajor(false, dxl, spanL, edgeLenL, xspan.EdgeCovL); + if (spanR.Increment > 0x40000) + EdgeParams_XMajor(true, dxr, spanR, edgeLenR, xspan.EdgeCovR); + else + EdgeParams_YMajor(true, dxr, spanR, edgeLenR, xspan.EdgeCovR); + } + + xspan.CovLInitial = (xspan.EdgeCovL >> 12) & 0x3FF; + if (xspan.CovLInitial == 0x3FF) + xspan.CovLInitial = 0; + xspan.CovRInitial = (xspan.EdgeCovR >> 12) & 0x3FF; + if (xspan.CovRInitial == 0x3FF) + xspan.CovRInitial = 0; + + xspan.X0 = xl; + xspan.X1 = xr + 1; + + uint polyalpha = ((polygon.Attr >> 16) & 0x1FU); + bool isWireframe = polyalpha == 0U; + + if (!isWireframe || (y == polygon.YTop || y == polygon.YBot - 1)) + xspan.Flags |= XSpanSetup_FillInside; + + xspan.InsideStart = xspan.X0 + edgeLenL; + if (xspan.InsideStart > xspan.X1) + xspan.InsideStart = xspan.X1; + xspan.InsideEnd = xspan.X1 - edgeLenR; + if (xspan.InsideEnd > xspan.X1) + xspan.InsideEnd = xspan.X1; + + bool isShadowMask = ((polygon.Attr & 0x3F000030U) == 0x00000030U); + bool fillAllEdges = polyalpha < 31 || (DispCnt & (3U<<4)) != 0U; + + if (fillAllEdges || spanL.X1 < spanL.X0 || spanL.Increment <= 0x40000) + xspan.Flags |= XSpanSetup_FillLeft; + if (fillAllEdges || (spanR.X1 >= spanR.X0 && spanR.Increment > 0x40000) || spanR.Increment == 0) + xspan.Flags |= XSpanSetup_FillRight; + + if (spanL.I0 == spanL.I1) + { + xspan.TexcoordU0 = spanL.TexcoordU0; + xspan.TexcoordV0 = spanL.TexcoordV0; + xspan.ColorR0 = spanL.ColorR0; + xspan.ColorG0 = spanL.ColorG0; + xspan.ColorB0 = spanL.ColorB0; + xspan.Z0 = spanL.Z0; + xspan.W0 = spanL.W0; + } + else + { + int i = (spanL.Increment > 0x40000 ? xl : y) - spanL.I0; + int ifactor = CalcYFactorY(spanL, i); + int idiff = spanL.I1 - spanL.I0; + +#ifdef ZBuffer + xspan.Z0 = int(InterpolateZZBuffer(spanL.Z0, spanL.Z1, i, spanL.IRecip, idiff)); +#endif +#ifdef WBuffer + xspan.Z0 = int(InterpolateZWBuffer(spanL.Z0, spanL.Z1, ifactor)); +#endif + + if (!spanL.Linear) + { + xspan.TexcoordU0 = InterpolateAttrPersp(spanL.TexcoordU0, spanL.TexcoordU1, ifactor); + xspan.TexcoordV0 = InterpolateAttrPersp(spanL.TexcoordV0, spanL.TexcoordV1, ifactor); + + xspan.ColorR0 = InterpolateAttrPersp(spanL.ColorR0, spanL.ColorR1, ifactor); + xspan.ColorG0 = InterpolateAttrPersp(spanL.ColorG0, spanL.ColorG1, ifactor); + xspan.ColorB0 = InterpolateAttrPersp(spanL.ColorB0, spanL.ColorB1, ifactor); + + xspan.W0 = InterpolateAttrPersp(spanL.W0, spanL.W1, ifactor); + } + else + { + xspan.TexcoordU0 = InterpolateAttrLinear(spanL.TexcoordU0, spanL.TexcoordU1, i, spanL.IRecip, idiff); + xspan.TexcoordV0 = InterpolateAttrLinear(spanL.TexcoordV0, spanL.TexcoordV1, i, spanL.IRecip, idiff); + + xspan.ColorR0 = InterpolateAttrLinear(spanL.ColorR0, spanL.ColorR1, i, spanL.IRecip, idiff); + xspan.ColorG0 = InterpolateAttrLinear(spanL.ColorG0, spanL.ColorG1, i, spanL.IRecip, idiff); + xspan.ColorB0 = InterpolateAttrLinear(spanL.ColorB0, spanL.ColorB1, i, spanL.IRecip, idiff); + + xspan.W0 = spanL.W0; // linear mode is only taken if W0 == W1 + } + } + + if (spanR.I0 == spanR.I1) + { + xspan.TexcoordU1 = spanR.TexcoordU0; + xspan.TexcoordV1 = spanR.TexcoordV0; + xspan.ColorR1 = spanR.ColorR0; + xspan.ColorG1 = spanR.ColorG0; + xspan.ColorB1 = spanR.ColorB0; + xspan.Z1 = spanR.Z0; + xspan.W1 = spanR.W0; + } + else + { + int i = (spanR.Increment > 0x40000 ? xr : y) - spanR.I0; + int ifactor = CalcYFactorY(spanR, i); + int idiff = spanR.I1 - spanR.I0; + + #ifdef ZBuffer + xspan.Z1 = int(InterpolateZZBuffer(spanR.Z0, spanR.Z1, i, spanR.IRecip, idiff)); + #endif + #ifdef WBuffer + xspan.Z1 = int(InterpolateZWBuffer(spanR.Z0, spanR.Z1, ifactor)); + #endif + + if (!spanR.Linear) + { + xspan.TexcoordU1 = InterpolateAttrPersp(spanR.TexcoordU0, spanR.TexcoordU1, ifactor); + xspan.TexcoordV1 = InterpolateAttrPersp(spanR.TexcoordV0, spanR.TexcoordV1, ifactor); + + xspan.ColorR1 = InterpolateAttrPersp(spanR.ColorR0, spanR.ColorR1, ifactor); + xspan.ColorG1 = InterpolateAttrPersp(spanR.ColorG0, spanR.ColorG1, ifactor); + xspan.ColorB1 = InterpolateAttrPersp(spanR.ColorB0, spanR.ColorB1, ifactor); + + xspan.W1 = int(InterpolateAttrPersp(spanR.W0, spanR.W1, ifactor)); + } + else + { + xspan.TexcoordU1 = InterpolateAttrLinear(spanR.TexcoordU0, spanR.TexcoordU1, i, spanR.IRecip, idiff); + xspan.TexcoordV1 = InterpolateAttrLinear(spanR.TexcoordV0, spanR.TexcoordV1, i, spanR.IRecip, idiff); + + xspan.ColorR1 = InterpolateAttrLinear(spanR.ColorR0, spanR.ColorR1, i, spanR.IRecip, idiff); + xspan.ColorG1 = InterpolateAttrLinear(spanR.ColorG0, spanR.ColorG1, i, spanR.IRecip, idiff); + xspan.ColorB1 = InterpolateAttrLinear(spanR.ColorB0, spanR.ColorB1, i, spanR.IRecip, idiff); + + xspan.W1 = spanR.W0; + } + } + + if (xspan.W0 == xspan.W1 && ((xspan.W0 | xspan.W1) & 0x7F) == 0) + { + xspan.Flags |= XSpanSetup_Linear; +// a bit hacky, but when wbuffering we only need to calculate xrecip for linear spans +#ifdef ZBuffer + } + { +#endif + uint r; + xspan.XRecip = int(Div(1U<<30, uint(xspan.X1 - xspan.X0), r)); + } + + XSpanSetups[gl_GlobalInvocationID.x] = xspan; +} + +)"; + +const std::string ClearIndirectWorkCount = + BinningBuffer + R"( + +layout (local_size_x = 32) in; + +void main() +{ + VariantWorkCount[gl_GlobalInvocationID.x] = uvec4(1, 1, 0, 0); +} + +)"; + +const std::string ClearCoarseBinMask = + BinningBuffer + R"( +layout (local_size_x = 32) in; + +void main() +{ + BinningMaskAndOffset[BinningCoarseMaskStart + gl_GlobalInvocationID.x*CoarseBinStride+0] = 0; + BinningMaskAndOffset[BinningCoarseMaskStart + gl_GlobalInvocationID.x*CoarseBinStride+1] = 0; +} + +)"; + +const std::string BinCombined = + PolygonBuffer + + BinningBuffer + + XSpanSetupBuffer + + WorkDescBuffer + R"( + +layout (local_size_x = 32) in; + +bool BinPolygon(Polygon polygon, ivec2 topLeft, ivec2 botRight) +{ + if (polygon.YTop > botRight.y || polygon.YBot <= topLeft.y) + return false; + + int polygonHeight = polygon.YBot - polygon.YTop; + + /* + All (good) polygons are convex. So the following holds true: + + Starting from the top most point where both edges originate + the X coordinate of the left edge will stay the same or falls until + the minimum X-axis coordinate is reached. Then it stays the same or + rises until the point it meets with the right edge. + + The same applies to the right edge, except that it first may rise or stay equal and + after the maximum point may only fall or stay the same. + + This means that for every tile which doesn't contain the point where the direction changes + we can just get the maximum point by sampling the top most and bottom most coordinate + within the tile. + + For a tile which is that the height of the direction change + + As a sidenote another consequence of this design decision is + that malformed polygons aren't binned properly. + + As a note bottom Y is exclusive! + */ + int polyInnerTopY = clamp(topLeft.y - polygon.YTop, 0, max(polygonHeight-1, 0)); + int polyInnerBotY = clamp(botRight.y - polygon.YTop, 0, max(polygonHeight-1, 0)); + + XSpanSetup xspanTop = XSpanSetups[polygon.FirstXSpan + polyInnerTopY]; + XSpanSetup xspanBot = XSpanSetups[polygon.FirstXSpan + polyInnerBotY]; + + int minXL; + if (polygon.XMinY >= topLeft.y && polygon.XMinY <= botRight.y) + minXL = polygon.XMin; + else + minXL = min(xspanTop.X0, xspanBot.X0); + + if (minXL > botRight.x) + return false; + + int maxXR; + if (polygon.XMaxY >= topLeft.y && polygon.XMaxY <= botRight.y) + maxXR = polygon.XMax; + else + maxXR = max(xspanTop.X1, xspanBot.X1) - 1; + + if (maxXR < topLeft.x) + return false; + + return true; +} + +shared uint mergedMaskShared; + +void main() +{ + int groupIdx = int(gl_WorkGroupID.x); + ivec2 coarseTile = ivec2(gl_WorkGroupID.yz); + +#if 0 + int localIdx = int(gl_SubGroupInvocationARB); +#else + int localIdx = int(gl_LocalInvocationIndex); + + if (localIdx == 0) + mergedMaskShared = 0U; + barrier(); +#endif + + int polygonIdx = groupIdx * 32 + localIdx; + + ivec2 coarseTopLeft = coarseTile * ivec2(CoarseTileW, CoarseTileH); + ivec2 coarseBotRight = coarseTopLeft + ivec2(CoarseTileW-1, CoarseTileH-1); + + bool binned = false; + if (polygonIdx < NumPolygons) + { + binned = BinPolygon(Polygons[polygonIdx], coarseTopLeft, coarseBotRight); + } + +#if 0 + uint mergedMask = unpackUint2x32(ballotARB(binned)).x; +#else + if (binned) + atomicOr(mergedMaskShared, 1U << localIdx); + barrier(); + uint mergedMask = mergedMaskShared; +#endif + + ivec2 fineTile = ivec2(localIdx & 0x7, localIdx >> 3); + + ivec2 fineTileTopLeft = coarseTopLeft + fineTile * ivec2(TileSize, TileSize); + ivec2 fineTileBotRight = fineTileTopLeft + ivec2(TileSize-1, TileSize-1); + + uint binnedMask = 0U; + while (mergedMask != 0U) + { + int bit = findLSB(mergedMask); + mergedMask &= ~(1U << bit); + + int polygonIdx = groupIdx * 32 + bit; + + if (BinPolygon(Polygons[polygonIdx], fineTileTopLeft, fineTileBotRight)) + binnedMask |= 1U << bit; + } + + int linearTile = fineTile.x + fineTile.y * TilesPerLine + coarseTile.x * CoarseTileCountX + coarseTile.y * TilesPerLine * CoarseTileCountY; + + BinningMaskAndOffset[BinningMaskStart + linearTile * BinStride + groupIdx] = binnedMask; + int coarseMaskIdx = linearTile * CoarseBinStride + (groupIdx >> 5); + if (binnedMask != 0U) + atomicOr(BinningMaskAndOffset[BinningCoarseMaskStart + coarseMaskIdx], 1U << (groupIdx & 0x1F)); + + if (binnedMask != 0U) + { + uint workOffset = atomicAdd(VariantWorkCount[0].w, uint(bitCount(binnedMask))); + BinningMaskAndOffset[BinningWorkOffsetsStart + linearTile * BinStride + groupIdx] = workOffset; + + uint tilePositionCombined = bitfieldInsert(fineTileTopLeft.x, fineTileTopLeft.y, 16, 16); + + int idx = 0; + while (binnedMask != 0U) + { + int bit = findLSB(binnedMask); + binnedMask &= ~(1U << bit); + + int polygonIdx = groupIdx * 32 + bit; + int variantIdx = Polygons[polygonIdx].Variant; + + int inVariantOffset = int(atomicAdd(VariantWorkCount[variantIdx].z, 1)); + WorkDescs[WorkDescsUnsortedStart + workOffset + idx] = uvec2(tilePositionCombined, bitfieldInsert(polygonIdx, inVariantOffset, 12, 20)); + + idx++; + } + } +} + +)"; + +const std::string CalcOffsets = + BinningBuffer + R"( + +layout (local_size_x = 32) in; + +void main() +{ + if (gl_GlobalInvocationID.x < NumVariants) + { + if (gl_GlobalInvocationID.x == 0) + { + // a bit of a cheat putting this here, but this shader won't run that often + SortWorkWorkCount = uvec4((VariantWorkCount[0].w + 31) / 32, 1, 1, 0); + } + SortedWorkOffset[gl_GlobalInvocationID.x] = atomicAdd(VariantWorkCount[1].w, VariantWorkCount[gl_GlobalInvocationID.x].z); + } +} + + +)"; + +const std::string SortWork = + PolygonBuffer + + BinningBuffer + + WorkDescBuffer + R"( + +layout (local_size_x = 32) in; + +void main() +{ + if (gl_GlobalInvocationID.x < VariantWorkCount[0].w) + { + uvec2 workDesc = WorkDescs[WorkDescsUnsortedStart + gl_GlobalInvocationID.x]; + int inVariantOffset = int(bitfieldExtract(workDesc.y, 12, 20)); + int polygonIdx = int(bitfieldExtract(workDesc.y, 0, 12)); + int variantIdx = Polygons[polygonIdx].Variant; + + int sortedIndex = int(SortedWorkOffset[variantIdx]) + inVariantOffset; + WorkDescs[WorkDescsSortedStart + sortedIndex] = uvec2(workDesc.x, bitfieldInsert(workDesc.y, gl_GlobalInvocationID.x, 12, 20)); + } +} + +)"; + +const std::string Rasterise = + PolygonBuffer + + WorkDescBuffer + + XSpanSetupBuffer + + BinningBuffer + + Tilebuffers + R"( + +layout (local_size_x = TileSize, local_size_y = TileSize) in; + +layout (binding = 0) uniform usampler2DArray CurrentTexture; + +layout (location = 0) uniform uint CurVariant; +layout (location = 1) uniform vec2 InvTextureSize; + +void main() +{ + uvec2 workDesc = WorkDescs[WorkDescsSortedStart + SortedWorkOffset[CurVariant] + gl_WorkGroupID.z]; + Polygon polygon = Polygons[bitfieldExtract(workDesc.y, 0, 12)]; + ivec2 position = ivec2(bitfieldExtract(workDesc.x, 0, 16), bitfieldExtract(workDesc.x, 16, 16)) + ivec2(gl_LocalInvocationID.xy); + int tileOffset = int(bitfieldExtract(workDesc.y, 12, 20)) * TileSize * TileSize + TileSize * int(gl_LocalInvocationID.y) + int(gl_LocalInvocationID.x); + + uint color = 0U; + if (position.y >= polygon.YTop && position.y < polygon.YBot) + { + XSpanSetup xspan = XSpanSetups[polygon.FirstXSpan + (position.y - polygon.YTop)]; + + bool insideLeftEdge = position.x < xspan.InsideStart; + bool insideRightEdge = position.x >= xspan.InsideEnd; + bool insidePolygonInside = !insideLeftEdge && !insideRightEdge; + + if (position.x >= xspan.X0 && position.x < xspan.X1 + && ((insideLeftEdge && (xspan.Flags & XSpanSetup_FillLeft) != 0U) + || (insideRightEdge && (xspan.Flags & XSpanSetup_FillRight) != 0U) + || (insidePolygonInside && (xspan.Flags & XSpanSetup_FillInside) != 0U))) + { + uint attr = 0; + if (position.y == polygon.YTop) + attr |= 0x4U; + else if (position.y == polygon.YBot - 1) + attr |= 0x8U; + + if (insideLeftEdge) + { + attr |= 0x1U; + + int cov = xspan.EdgeCovL; + if (cov < 0) + { + int xcov = xspan.CovLInitial + (xspan.EdgeCovL & 0x3FF) * (position.x - xspan.X0); + cov = min(xcov >> 5, 31); + } + + attr |= uint(cov) << 8; + } + else if (insideRightEdge) + { + attr |= 0x2U; + + int cov = xspan.EdgeCovR; + if (cov < 0) + { + int xcov = xspan.CovRInitial + (xspan.EdgeCovR & 0x3FF) * (position.x - xspan.InsideEnd); + cov = max(0x1F - (xcov >> 5), 0); + } + + attr |= uint(cov) << 8; + } + + uint z; + int u, v, vr, vg, vb; + + if (xspan.X0 == xspan.X1) + { + z = xspan.Z0; + u = xspan.TexcoordU0; + v = xspan.TexcoordV0; + vr = xspan.ColorR0; + vg = xspan.ColorG0; + vb = xspan.ColorB0; + } + else + { + int ifactor = CalcYFactorX(xspan, position.x); + int idiff = xspan.X1 - xspan.X0; + int i = position.x - xspan.X0; + +#ifdef ZBuffer + z = InterpolateZZBuffer(xspan.Z0, xspan.Z1, i, xspan.XRecip, idiff); +#endif +#ifdef WBuffer + z = InterpolateZWBuffer(xspan.Z0, xspan.Z1, ifactor); +#endif + if ((xspan.Flags & XSpanSetup_Linear) == 0U) + { + u = InterpolateAttrPersp(xspan.TexcoordU0, xspan.TexcoordU1, ifactor); + v = InterpolateAttrPersp(xspan.TexcoordV0, xspan.TexcoordV1, ifactor); + + vr = InterpolateAttrPersp(xspan.ColorR0, xspan.ColorR1, ifactor); + vg = InterpolateAttrPersp(xspan.ColorG0, xspan.ColorG1, ifactor); + vb = InterpolateAttrPersp(xspan.ColorB0, xspan.ColorB1, ifactor); + } + else + { + u = InterpolateAttrLinear(xspan.TexcoordU0, xspan.TexcoordU1, i, xspan.XRecip, idiff); + v = InterpolateAttrLinear(xspan.TexcoordV0, xspan.TexcoordV1, i, xspan.XRecip, idiff); + + vr = InterpolateAttrLinear(xspan.ColorR0, xspan.ColorR1, i, xspan.XRecip, idiff); + vg = InterpolateAttrLinear(xspan.ColorG0, xspan.ColorG1, i, xspan.XRecip, idiff); + vb = InterpolateAttrLinear(xspan.ColorB0, xspan.ColorB1, i, xspan.XRecip, idiff); + } + } + +#ifndef ShadowMask + vr >>= 3; + vg >>= 3; + vb >>= 3; + + uint r, g, b, a; + uint polyalpha = bitfieldExtract(polygon.Attr, 16, 5); + +#ifdef Toon + uint tooncolor = ToonTable[vr >> 1].r; + vr = int(bitfieldExtract(tooncolor, 0, 8)); + vg = int(bitfieldExtract(tooncolor, 8, 8)); + vb = int(bitfieldExtract(tooncolor, 16, 8)); +#endif +#ifdef Highlight + vg = vr; + vb = vr; +#endif + +#ifdef NoTexture + a = int(polyalpha); +#endif + r = vr; + g = vg; + b = vb; + +#ifdef UseTexture + vec2 uvf = vec2(ivec2(u, v)) * vec2(1.0 / 16.0) * InvTextureSize; + + uvec4 texcolor = texture(CurrentTexture, vec3(uvf, polygon.TextureLayer)); +#ifdef Decal + if (texcolor.a == 31) + { + r = int(texcolor.r); + g = int(texcolor.g); + b = int(texcolor.b); + } + else if (texcolor.a > 0) + { + r = int((texcolor.r * texcolor.a) + (vr * (31-texcolor.a))) >> 5; + g = int((texcolor.g * texcolor.a) + (vg * (31-texcolor.a))) >> 5; + b = int((texcolor.b * texcolor.a) + (vb * (31-texcolor.a))) >> 5; + } + a = int(polyalpha); +#endif +#if defined(Modulate) || defined(Toon) || defined(Highlight) + r = int((texcolor.r+1) * (vr+1) - 1) >> 6; + g = int((texcolor.g+1) * (vg+1) - 1) >> 6; + b = int((texcolor.b+1) * (vb+1) - 1) >> 6; + a = int((texcolor.a+1) * (polyalpha+1) - 1) >> 5; +#endif +#endif + +#ifdef Highlight + uint tooncolor = ToonTable[vr >> 1].r; + + r = min(r + int(bitfieldExtract(tooncolor, 0, 8)), 63); + g = min(g + int(bitfieldExtract(tooncolor, 8, 8)), 63); + b = min(b + int(bitfieldExtract(tooncolor, 16, 8)), 63); +#endif + + if (polyalpha == 0) + a = 31; + + if (a > AlphaRef) + { + color = r | (g << 8) | (b << 16) | (a << 24); + + DepthTiles[tileOffset] = z; + AttrTiles[tileOffset] = attr; + } +#else + color = 0xFFFFFFFF; // doesn't really matter as long as it's not 0 + DepthTiles[tileOffset] = z; +#endif + } + } + + ColorTiles[tileOffset] = color; +} + +)"; + +const std::string DepthBlend = + PolygonBuffer + + Tilebuffers + + ResultBuffer + + BinningBuffer + R"( + +layout (local_size_x = TileSize, local_size_y = TileSize) in; + +void PlotTranslucent(inout uint color, inout uint depth, inout uint attr, bool isShadow, uint tileColor, uint srcA, uint tileDepth, uint srcAttr, bool writeDepth) +{ + uint blendAttr = (srcAttr & 0xE0F0U) | ((srcAttr >> 8) & 0xFF0000U) | (1U<<22) | (attr & 0xFF001F0FU); + + if ((!isShadow || (attr & (1U<<22)) != 0U) + ? (attr & 0x007F0000U) != (blendAttr & 0x007F0000U) + : (attr & 0x3F000000U) != (srcAttr & 0x3F000000U)) + { + // le blend + if (writeDepth) + depth = tileDepth; + + if ((attr & (1U<<15)) == 0) + blendAttr &= ~(1U<<15); + attr = blendAttr; + + uint srcRB = tileColor & 0x3F003FU; + uint srcG = tileColor & 0x003F00U; + uint dstRB = color & 0x3F003FU; + uint dstG = color & 0x003F00U; + uint dstA = color & 0x1F000000U; + + uint alpha = (srcA >> 24) + 1; + if (dstA != 0) + { + srcRB = ((srcRB * alpha) + (dstRB * (32-alpha))) >> 5; + srcG = ((srcG * alpha) + (dstG * (32-alpha))) >> 5; + } + + color = (srcRB & 0x3F003FU) | (srcG & 0x003F00U) | max(dstA, srcA); + } +} + +void ProcessCoarseMask(int linearTile, uint coarseMask, uint coarseOffset, + inout uvec2 color, inout uvec2 depth, inout uvec2 attr, inout uint stencil, + inout bool prevIsShadowMask) +{ + int tileInnerOffset = int(gl_LocalInvocationID.x) + int(gl_LocalInvocationID.y) * TileSize; + + while (coarseMask != 0U) + { + uint coarseBit = findLSB(coarseMask); + coarseMask &= ~(1U << coarseBit); + + uint tileOffset = linearTile * BinStride + coarseBit + coarseOffset; + + uint fineMask = BinningMaskAndOffset[BinningMaskStart + tileOffset]; + uint workIdx = BinningMaskAndOffset[BinningWorkOffsetsStart + tileOffset]; + + while (fineMask != 0U) + { + uint fineIdx = findLSB(fineMask); + fineMask &= ~(1U << fineIdx); + + uint pixelindex = tileInnerOffset + workIdx * TileSize * TileSize; + uint tileColor = ColorTiles[pixelindex]; + workIdx++; + + uint polygonIdx = fineIdx + (coarseBit + coarseOffset) * 32; + + if (tileColor != 0U) + { + uint polygonAttr = Polygons[polygonIdx].Attr; + + bool isShadowMask = ((polygonAttr & 0x3F000030U) == 0x00000030U); + bool prevIsShadowMaskOld = prevIsShadowMask; + prevIsShadowMask = isShadowMask; + + bool equalDepthTest = (polygonAttr & (1U << 14)) != 0U; + + uint tileDepth = DepthTiles[pixelindex]; + uint tileAttr = AttrTiles[pixelindex]; + + uint dstattr = attr.x; + + if (!isShadowMask) + { + bool isShadow = (polygonAttr & 0x30U) == 0x30U; + + bool writeSecondLayer = false; + + if (isShadow) + { + if (stencil == 0U) + continue; + if ((stencil & 1U) == 0U) + writeSecondLayer = true; + if ((stencil & 2U) == 0U) + dstattr &= ~0x3U; + } + + uint dstDepth = writeSecondLayer ? depth.y : depth.x; + if (!(equalDepthTest +#ifdef WBuffer + ? dstDepth - tileDepth + 0xFFU <= 0x1FE +#endif +#ifdef ZBuffer + ? dstDepth - tileDepth + 0x200 <= 0x400 +#endif + : tileDepth < dstDepth)) + { + if ((dstattr & 0x3U) == 0U || writeSecondLayer) + continue; + + writeSecondLayer = true; + dstattr = attr.y; + if (!(equalDepthTest +#ifdef WBuffer + ? depth.y - tileDepth + 0xFFU <= 0x1FE +#endif +#ifdef ZBuffer + ? depth.y - tileDepth + 0x200 <= 0x400 +#endif + : tileDepth < depth.y)) + continue; + } + + uint srcAttr = (polygonAttr & 0x3F008000U); + + uint srcA = tileColor & 0x1F000000U; + if (srcA == 0x1F000000U) + { + srcAttr |= tileAttr; + + if (!writeSecondLayer) + { + if ((srcAttr & 0x3U) != 0U) + { + color.y = color.x; + depth.y = depth.x; + attr.y = attr.x; + } + + color.x = tileColor; + depth.x = tileDepth; + attr.x = srcAttr; + } + else + { + color.y = tileColor; + depth.y = tileDepth; + attr.y = srcAttr; + } + } + else + { + bool writeDepth = (polygonAttr & (1U<<11)) != 0; + + if (!writeSecondLayer) + { + // blend into both layers + PlotTranslucent(color.x, depth.x, attr.x, isShadow, tileColor, srcA, tileDepth, srcAttr, writeDepth); + } + if (writeSecondLayer || (dstattr & 0x3U) != 0U) + { + PlotTranslucent(color.y, depth.y, attr.y, isShadow, tileColor, srcA, tileDepth, srcAttr, writeDepth); + } + } + } + else + { + if (!prevIsShadowMaskOld) + stencil = 0; + + if (!(equalDepthTest +#ifdef WBuffer + ? depth.x - tileDepth + 0xFFU <= 0x1FE +#endif +#ifdef ZBuffer + ? depth.x - tileDepth + 0x200 <= 0x400 +#endif + : tileDepth < depth.x)) + stencil = 0x1U; + + if ((dstattr & 0x3U) != 0U) + { + if (!(equalDepthTest +#ifdef WBuffer + ? depth.y - tileDepth + 0xFFU <= 0x1FE +#endif +#ifdef ZBuffer + ? depth.y - tileDepth + 0x200 <= 0x400 +#endif + : tileDepth < depth.y)) + stencil |= 0x2U; + } + } + } + } + } +} + +void main() +{ + int linearTile = int(gl_WorkGroupID.x + (gl_WorkGroupID.y * TilesPerLine)); + + uint coarseMaskLo = BinningMaskAndOffset[BinningCoarseMaskStart + linearTile*CoarseBinStride + 0]; + uint coarseMaskHi = BinningMaskAndOffset[BinningCoarseMaskStart + linearTile*CoarseBinStride + 1]; + + uvec2 color = uvec2(ClearColor, 0U); + uvec2 depth = uvec2(ClearDepth, 0U); + uvec2 attr = uvec2(ClearAttr, 0U); + uint stencil = 0U; + bool prevIsShadowMask = false; + + ProcessCoarseMask(linearTile, coarseMaskLo, 0, color, depth, attr, stencil, prevIsShadowMask); + ProcessCoarseMask(linearTile, coarseMaskHi, BinStride/2, color, depth, attr, stencil, prevIsShadowMask); + + int resultOffset = int(gl_GlobalInvocationID.x) + int(gl_GlobalInvocationID.y) * ScreenWidth; + ResultValue[ResultColorStart+resultOffset] = color.x; + ResultValue[ResultColorStart+resultOffset+FramebufferStride] = color.y; + ResultValue[ResultDepthStart+resultOffset] = depth.x; + ResultValue[ResultDepthStart+resultOffset+FramebufferStride] = depth.y; + ResultValue[ResultAttrStart+resultOffset] = attr.x; + ResultValue[ResultAttrStart+resultOffset+FramebufferStride] = attr.y; +} + +)"; + +const std::string FinalPass = + ResultBuffer + R"( + +layout (local_size_x = 32) in; + +layout (binding = 0, rgba8) writeonly uniform image2D FinalFB; +layout (binding = 1, rgba8ui) writeonly uniform uimage2D LowResFB; + +uint BlendFog(uint color, uint depth) +{ + uint densityid = 0, densityfrac = 0; + + if (depth >= FogOffset) + { + depth -= FogOffset; + depth = (depth >> 2) << FogShift; + + densityid = depth >> 17; + if (densityid >= 32) + { + densityid = 32; + densityfrac = 0; + } + else + { + densityfrac = depth & 0x1FFFFU; + } + } + + uint density = + ((ToonTable[densityid].g * (0x20000U-densityfrac)) + + (ToonTable[densityid+1].g * densityfrac)) >> 17; + density = min(density, 128U); + + uint colorRB = color & 0x3F003FU; + uint colorGA = (color >> 8) & 0x3F003FU; + + uint fogRB = FogColor & 0x3F003FU; + uint fogGA = (FogColor >> 8) & 0x1F003FU; + + uint finalColorRB = ((fogRB * density) + (colorRB * (128-density))) >> 7; + uint finalColorGA = ((fogGA * density) + (colorGA * (128-density))) >> 7; + + finalColorRB &= 0x3F003FU; + finalColorGA &= 0x1F003FU; + + return (DispCnt & (1U<<6)) != 0 + ? (bitfieldInsert(color, finalColorGA >> 16, 24, 8)) + : (finalColorRB | (finalColorGA << 8)); +} + +void main() +{ + int srcX = int(gl_GlobalInvocationID.x); + int resultOffset = int(srcX) + int(gl_GlobalInvocationID.y) * ScreenWidth; + + uvec2 color = uvec2(ResultValue[resultOffset+ResultColorStart], ResultValue[resultOffset+FramebufferStride+ResultColorStart]); + uvec2 depth = uvec2(ResultValue[resultOffset+ResultDepthStart], ResultValue[resultOffset+FramebufferStride+ResultDepthStart]); + uvec2 attr = uvec2(ResultValue[resultOffset+ResultAttrStart], ResultValue[resultOffset+FramebufferStride+ResultAttrStart]); + +#ifdef EdgeMarking + if ((attr.x & 0xFU) != 0U) + { + uvec4 otherAttr = uvec4(ClearAttr); + uvec4 otherDepth = uvec4(ClearDepth); + + if (srcX > 0U) + { + otherAttr.x = ResultValue[resultOffset-1+ResultAttrStart]; + otherDepth.x = ResultValue[resultOffset-1+ResultDepthStart]; + } + if (srcX < ScreenWidth-1) + { + otherAttr.y = ResultValue[resultOffset+1+ResultAttrStart]; + otherDepth.y = ResultValue[resultOffset+1+ResultDepthStart]; + } + if (gl_GlobalInvocationID.y > 0U) + { + otherAttr.z = ResultValue[resultOffset-ScreenWidth+ResultAttrStart]; + otherDepth.z = ResultValue[resultOffset-ScreenWidth+ResultDepthStart]; + } + if (gl_GlobalInvocationID.y < ScreenHeight-1) + { + otherAttr.w = ResultValue[resultOffset+ScreenWidth+ResultAttrStart]; + otherDepth.w = ResultValue[resultOffset+ScreenWidth+ResultDepthStart]; + } + + uint polyId = bitfieldExtract(attr.x, 24, 6); + uvec4 otherPolyId = bitfieldExtract(otherAttr, 24, 6); + + bvec4 polyIdMismatch = notEqual(uvec4(polyId), otherPolyId); + bvec4 nearer = lessThan(uvec4(depth.x), otherDepth); + + if ((polyIdMismatch.x && nearer.x) + || (polyIdMismatch.y && nearer.y) + || (polyIdMismatch.z && nearer.z) + || (polyIdMismatch.w && nearer.w)) + { + color.x = ToonTable[polyId >> 3].b | (color.x & 0xFF000000U); + attr.x = (attr.x & 0xFFFFE0FFU) | 0x00001000U; + } + } +#endif + +#ifdef Fog + if ((attr.x & (1U<<15)) != 0U) + { + color.x = BlendFog(color.x, depth.x); + } + + if ((attr.x & 0xFU) != 0 && (attr.y & (1U<<15)) != 0U) + { + color.y = BlendFog(color.y, depth.y); + } +#endif + +#ifdef AntiAliasing + // resolve anti-aliasing + if ((attr.x & 0x3U) != 0) + { + uint coverage = (attr.x >> 8) & 0x1FU; + + if (coverage != 0) + { + uint topRB = color.x & 0x3F003FU; + uint topG = color.x & 0x003F00U; + uint topA = bitfieldExtract(color.x, 24, 5); + + uint botRB = color.y & 0x3F003FU; + uint botG = color.y & 0x003F00U; + uint botA = bitfieldExtract(color.y, 24, 5); + + coverage++; + + if (botA > 0) + { + topRB = ((topRB * coverage) + (botRB * (32-coverage))) >> 5; + topG = ((topG * coverage) + (botG * (32-coverage))) >> 5; + + topRB &= 0x3F003FU; + topG &= 0x003F00U; + } + + topA = ((topA * coverage) + (botA * (32-coverage))) >> 5; + + color.x = topRB | topG | (topA << 24); + } + else + { + color.x = color.y; + } + } +#endif + +// if (bitfieldExtract(color.x, 24, 8) != 0U) +// color.x |= 0x40000000U; +// else +// color.x = 0U; + + //if ((gl_GlobalInvocationID.y % 8) == 7 || (gl_GlobalInvocationID.y % 8) == 7) + // color.x = 0x1F00001FU | 0x40000000U; + + vec4 result = vec4(bitfieldExtract(color.x, 16, 8), bitfieldExtract(color.x, 8, 8), color.x & 0x3FU, bitfieldExtract(color.x, 24, 8)); + result /= vec4(63.0, 63.0, 63.0, 31.0); + imageStore(FinalFB, ivec2(gl_GlobalInvocationID.xy), result); + + // It's a division by constant, so using the builtin division is fine + const int scale = ScreenWidth/256; + ivec2 lowresCoordinate = ivec2(gl_GlobalInvocationID.xy) / scale; + ivec2 lowresCoordinateRest = ivec2(gl_GlobalInvocationID.xy) % scale; + if (lowresCoordinateRest == ivec2(0, 0)) + { + uvec4 color8; + color8.x = bitfieldExtract(color.x, 0, 8); + color8.y = bitfieldExtract(color.x, 8, 8); + color8.z = bitfieldExtract(color.x, 16, 8); + color8.w = bitfieldExtract(color.x, 24, 8); + imageStore(LowResFB, lowresCoordinate, color8); + } +} + +)"; + +} + +} + +#endif \ No newline at end of file diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 9648be3..825a471 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -28,46 +28,32 @@ namespace GPU3D { -bool GLRenderer::BuildRenderShader(u32 flags, const char* vs, const char* fs) +bool GLRenderer::BuildRenderShader(u32 flags, const std::string& vs, const std::string& fs) { char shadername[32]; sprintf(shadername, "RenderShader%02X", flags); int headerlen = strlen(kShaderHeader); - int vslen = strlen(vs); - int vsclen = strlen(kRenderVSCommon); - char* vsbuf = new char[headerlen + vsclen + vslen + 1]; - strcpy(&vsbuf[0], kShaderHeader); - strcpy(&vsbuf[headerlen], kRenderVSCommon); - strcpy(&vsbuf[headerlen + vsclen], vs); + std::string vsbuf; + vsbuf += kShaderHeader; + vsbuf += kRenderVSCommon; + vsbuf += vs; - int fslen = strlen(fs); - int fsclen = strlen(kRenderFSCommon); - char* fsbuf = new char[headerlen + fsclen + fslen + 1]; - strcpy(&fsbuf[0], kShaderHeader); - strcpy(&fsbuf[headerlen], kRenderFSCommon); - strcpy(&fsbuf[headerlen + fsclen], fs); + std::string fsbuf; + fsbuf += kShaderHeader; + fsbuf += kRenderFSCommon; + fsbuf += fs; - bool ret = OpenGL::BuildShaderProgram(vsbuf, fsbuf, RenderShader[flags], shadername); - - delete[] vsbuf; - delete[] fsbuf; + GLuint prog; + bool ret = OpenGL::CompileVertexFragmentProgram(prog, + vsbuf, fsbuf, + shadername, + {{"vPosition", 0}, {"vColor", 1}, {"vTexcoord", 2}, {"vPolygonAttr", 3}}, + {{"oColor", 0}, {"oAttr", 1}}); if (!ret) return false; - GLuint prog = RenderShader[flags][2]; - - glBindAttribLocation(prog, 0, "vPosition"); - glBindAttribLocation(prog, 1, "vColor"); - glBindAttribLocation(prog, 2, "vTexcoord"); - glBindAttribLocation(prog, 3, "vPolygonAttr"); - glBindFragDataLocation(prog, 0, "oColor"); - glBindFragDataLocation(prog, 1, "oAttr"); - - if (!OpenGL::LinkShaderProgram(RenderShader[flags])) - return false; - GLint uni_id = glGetUniformBlockIndex(prog, "uConfig"); glUniformBlockBinding(prog, uni_id, 0); @@ -78,13 +64,15 @@ bool GLRenderer::BuildRenderShader(u32 flags, const char* vs, const char* fs) uni_id = glGetUniformLocation(prog, "TexPalMem"); glUniform1i(uni_id, 1); + RenderShader[flags] = prog; + return true; } void GLRenderer::UseRenderShader(u32 flags) { if (CurShaderID == flags) return; - glUseProgram(RenderShader[flags][2]); + glUseProgram(RenderShader[flags]); CurShaderID = flags; } @@ -118,21 +106,17 @@ std::unique_ptr<GLRenderer> GLRenderer::New() noexcept glDepthRange(0, 1); glClearDepth(1.0); - - if (!OpenGL::BuildShaderProgram(kClearVS, kClearFS, result->ClearShaderPlain, "ClearShader")) + if (!OpenGL::CompileVertexFragmentProgram(result->ClearShaderPlain, + kClearVS, kClearFS, + "ClearShader", + {{"vPosition", 0}}, + {{"oColor", 0}, {"oAttr", 1}})) return nullptr; - glBindAttribLocation(result->ClearShaderPlain[2], 0, "vPosition"); - glBindFragDataLocation(result->ClearShaderPlain[2], 0, "oColor"); - glBindFragDataLocation(result->ClearShaderPlain[2], 1, "oAttr"); - - if (!OpenGL::LinkShaderProgram(result->ClearShaderPlain)) - return nullptr; - - result->ClearUniformLoc[0] = glGetUniformLocation(result->ClearShaderPlain[2], "uColor"); - result->ClearUniformLoc[1] = glGetUniformLocation(result->ClearShaderPlain[2], "uDepth"); - result->ClearUniformLoc[2] = glGetUniformLocation(result->ClearShaderPlain[2], "uOpaquePolyID"); - result->ClearUniformLoc[3] = glGetUniformLocation(result->ClearShaderPlain[2], "uFogFlag"); + result->ClearUniformLoc[0] = glGetUniformLocation(result->ClearShaderPlain, "uColor"); + result->ClearUniformLoc[1] = glGetUniformLocation(result->ClearShaderPlain, "uDepth"); + result->ClearUniformLoc[2] = glGetUniformLocation(result->ClearShaderPlain, "uOpaquePolyID"); + result->ClearUniformLoc[3] = glGetUniformLocation(result->ClearShaderPlain, "uFogFlag"); memset(result->RenderShader, 0, sizeof(RenderShader)); @@ -160,42 +144,35 @@ std::unique_ptr<GLRenderer> GLRenderer::New() noexcept if (!result->BuildRenderShader(RenderFlag_ShadowMask | RenderFlag_WBuffer, kRenderVS_W, kRenderFS_WSM)) return nullptr; - if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, result->FinalPassEdgeShader, "FinalPassEdgeShader")) + if (!OpenGL::CompileVertexFragmentProgram(result->FinalPassEdgeShader, + kFinalPassVS, kFinalPassEdgeFS, + "FinalPassEdgeShader", + {{"vPosition", 0}}, + {{"oColor", 0}})) return nullptr; - - if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, result->FinalPassFogShader, "FinalPassFogShader")) + if (!OpenGL::CompileVertexFragmentProgram(result->FinalPassFogShader, + kFinalPassVS, kFinalPassFogFS, + "FinalPassFogShader", + {{"vPosition", 0}}, + {{"oColor", 0}})) return nullptr; - glBindAttribLocation(result->FinalPassEdgeShader[2], 0, "vPosition"); - glBindFragDataLocation(result->FinalPassEdgeShader[2], 0, "oColor"); - - if (!OpenGL::LinkShaderProgram(result->FinalPassEdgeShader)) - return nullptr; + GLuint uni_id = glGetUniformBlockIndex(result->FinalPassEdgeShader, "uConfig"); + glUniformBlockBinding(result->FinalPassEdgeShader, uni_id, 0); - GLint uni_id = glGetUniformBlockIndex(result->FinalPassEdgeShader[2], "uConfig"); - glUniformBlockBinding(result->FinalPassEdgeShader[2], uni_id, 0); - - glUseProgram(result->FinalPassEdgeShader[2]); - - uni_id = glGetUniformLocation(result->FinalPassEdgeShader[2], "DepthBuffer"); + glUseProgram(result->FinalPassEdgeShader); + uni_id = glGetUniformLocation(result->FinalPassEdgeShader, "DepthBuffer"); glUniform1i(uni_id, 0); - uni_id = glGetUniformLocation(result->FinalPassEdgeShader[2], "AttrBuffer"); + uni_id = glGetUniformLocation(result->FinalPassEdgeShader, "AttrBuffer"); glUniform1i(uni_id, 1); - glBindAttribLocation(result->FinalPassFogShader[2], 0, "vPosition"); - glBindFragDataLocation(result->FinalPassFogShader[2], 0, "oColor"); + uni_id = glGetUniformBlockIndex(result->FinalPassFogShader, "uConfig"); + glUniformBlockBinding(result->FinalPassFogShader, uni_id, 0); - if (!OpenGL::LinkShaderProgram(result->FinalPassFogShader)) - return nullptr; - - uni_id = glGetUniformBlockIndex(result->FinalPassFogShader[2], "uConfig"); - glUniformBlockBinding(result->FinalPassFogShader[2], uni_id, 0); - - glUseProgram(result->FinalPassFogShader[2]); - - uni_id = glGetUniformLocation(result->FinalPassFogShader[2], "DepthBuffer"); + glUseProgram(result->FinalPassFogShader); + uni_id = glGetUniformLocation(result->FinalPassFogShader, "DepthBuffer"); glUniform1i(uni_id, 0); - uni_id = glGetUniformLocation(result->FinalPassFogShader[2], "AttrBuffer"); + uni_id = glGetUniformLocation(result->FinalPassFogShader, "AttrBuffer"); glUniform1i(uni_id, 1); @@ -248,29 +225,26 @@ std::unique_ptr<GLRenderer> GLRenderer::New() noexcept glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result->IndexBufferID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(IndexBuffer), nullptr, GL_DYNAMIC_DRAW); - glGenFramebuffers(4, &result->FramebufferID[0]); - glBindFramebuffer(GL_FRAMEBUFFER, result->FramebufferID[0]); - - glGenTextures(8, &result->FramebufferTex[0]); - result->FrontBuffer = 0; + glGenFramebuffers(1, &result->MainFramebuffer); // color buffers - SetupDefaultTexParams(result->FramebufferTex[0]); - SetupDefaultTexParams(result->FramebufferTex[1]); + glGenTextures(1, &result->ColorBufferTex); + SetupDefaultTexParams(result->ColorBufferTex); // depth/stencil buffer - SetupDefaultTexParams(result->FramebufferTex[4]); - SetupDefaultTexParams(result->FramebufferTex[6]); + glGenTextures(1, &result->DepthBufferTex); + SetupDefaultTexParams(result->DepthBufferTex); // attribute buffer // R: opaque polyID (for edgemarking) // G: edge flag // B: fog flag - SetupDefaultTexParams(result->FramebufferTex[5]); - SetupDefaultTexParams(result->FramebufferTex[7]); + glGenTextures(1, &result->AttrBufferTex); + SetupDefaultTexParams(result->AttrBufferTex); // downscale framebuffer for display capture (always 256x192) - SetupDefaultTexParams(result->FramebufferTex[3]); + glGenTextures(1, &result->DownScaleBufferTex); + SetupDefaultTexParams(result->DownScaleBufferTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 192, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glEnable(GL_BLEND); @@ -308,8 +282,12 @@ GLRenderer::~GLRenderer() glDeleteTextures(1, &TexMemID); glDeleteTextures(1, &TexPalMemID); - glDeleteFramebuffers(4, &FramebufferID[0]); - glDeleteTextures(8, &FramebufferTex[0]); + glDeleteFramebuffers(1, &MainFramebuffer); + glDeleteFramebuffers(1, &DownscaleFramebuffer); + glDeleteTextures(1, &ColorBufferTex); + glDeleteTextures(1, &DepthBufferTex); + glDeleteTextures(1, &AttrBufferTex); + glDeleteTextures(1, &DownScaleBufferTex); glDeleteVertexArrays(1, &VertexArrayID); glDeleteBuffers(1, &VertexBufferID); @@ -320,8 +298,8 @@ GLRenderer::~GLRenderer() for (int i = 0; i < 16; i++) { - if (!RenderShader[i][2]) continue; - OpenGL::DeleteShaderProgram(RenderShader[i]); + if (!RenderShader[i]) continue; + glDeleteProgram(RenderShader[i]); } } @@ -339,40 +317,25 @@ void GLRenderer::SetRenderSettings(GPU::RenderSettings& settings) ScreenW = 256 * scale; ScreenH = 192 * scale; - glBindTexture(GL_TEXTURE_2D, FramebufferTex[0]); + glBindTexture(GL_TEXTURE_2D, ColorBufferTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ScreenW, ScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glBindTexture(GL_TEXTURE_2D, FramebufferTex[1]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ScreenW, ScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - glBindTexture(GL_TEXTURE_2D, FramebufferTex[4]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, ScreenW, ScreenH, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); - glBindTexture(GL_TEXTURE_2D, FramebufferTex[5]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ScreenW, ScreenH, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - glBindTexture(GL_TEXTURE_2D, FramebufferTex[6]); + glBindTexture(GL_TEXTURE_2D, DepthBufferTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, ScreenW, ScreenH, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); - glBindTexture(GL_TEXTURE_2D, FramebufferTex[7]); + glBindTexture(GL_TEXTURE_2D, AttrBufferTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ScreenW, ScreenH, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[3]); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[3], 0); + glBindFramebuffer(GL_FRAMEBUFFER, DownscaleFramebuffer); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, DownScaleBufferTex, 0); GLenum fbassign[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[0]); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[0], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, FramebufferTex[4], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, FramebufferTex[5], 0); - glDrawBuffers(2, fbassign); - - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[1]); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, FramebufferTex[1], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, FramebufferTex[6], 0); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, FramebufferTex[7], 0); + glBindFramebuffer(GL_FRAMEBUFFER, MainFramebuffer); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ColorBufferTex, 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, DepthBufferTex, 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, AttrBufferTex, 0); glDrawBuffers(2, fbassign); - glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[0]); - glBindBuffer(GL_PIXEL_PACK_BUFFER, PixelbufferID); glBufferData(GL_PIXEL_PACK_BUFFER, 256*192*4, NULL, GL_DYNAMIC_READ); @@ -1081,9 +1044,9 @@ void GLRenderer::RenderSceneChunk(int y, int h) glStencilMask(0); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, FramebufferTex[FrontBuffer ? 6 : 4]); + glBindTexture(GL_TEXTURE_2D, DepthBufferTex); glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, FramebufferTex[FrontBuffer ? 7 : 5]); + glBindTexture(GL_TEXTURE_2D, AttrBufferTex); glBindBuffer(GL_ARRAY_BUFFER, ClearVertexBufferID); glBindVertexArray(ClearVertexArrayID); @@ -1093,7 +1056,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) // edge marking // TODO: depth/polyid values at screen edges - glUseProgram(FinalPassEdgeShader[2]); + glUseProgram(FinalPassEdgeShader); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); @@ -1104,7 +1067,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) { // fog - glUseProgram(FinalPassFogShader[2]); + glUseProgram(FinalPassFogShader); if (RenderDispCnt & (1<<6)) glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA); @@ -1132,7 +1095,7 @@ void GLRenderer::RenderFrame() CurShaderID = -1; glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FramebufferID[FrontBuffer]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, MainFramebuffer); ShaderConfig.uScreenSize[0] = ScreenW; ShaderConfig.uScreenSize[1] = ScreenH; @@ -1238,7 +1201,7 @@ void GLRenderer::RenderFrame() // TODO: check whether 'clear polygon ID' affects translucent polyID // (for example when alpha is 1..30) { - glUseProgram(ClearShaderPlain[2]); + glUseProgram(ClearShaderPlain); glDepthFunc(GL_ALWAYS); u32 r = RenderClearAttr1 & 0x1F; @@ -1298,22 +1261,18 @@ void GLRenderer::RenderFrame() RenderSceneChunk(0, 192); } - - FrontBuffer = FrontBuffer ? 0 : 1; } void GLRenderer::PrepareCaptureFrame() { - // TODO: make sure this picks the right buffer when doing antialiasing - int original_fb = FrontBuffer^1; - - glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferID[original_fb]); + glBindFramebuffer(GL_READ_FRAMEBUFFER, MainFramebuffer); glReadBuffer(GL_COLOR_ATTACHMENT0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FramebufferID[3]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, DownscaleFramebuffer); glDrawBuffer(GL_COLOR_ATTACHMENT0); glBlitFramebuffer(0, 0, ScreenW, ScreenH, 0, 0, 256, 192, GL_COLOR_BUFFER_BIT, GL_NEAREST); - glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferID[3]); + glBindBuffer(GL_PIXEL_PACK_BUFFER, PixelbufferID); + glBindFramebuffer(GL_READ_FRAMEBUFFER, DownscaleFramebuffer); glReadPixels(0, 0, 256, 192, GL_BGRA, GL_UNSIGNED_BYTE, NULL); } @@ -1323,6 +1282,7 @@ u32* GLRenderer::GetLine(int line) if (line == 0) { + glBindBuffer(GL_PIXEL_PACK_BUFFER, PixelbufferID); u8* data = (u8*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (data) memcpy(&Framebuffer[stride*0], data, 4*stride*192); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); @@ -1342,7 +1302,7 @@ u32* GLRenderer::GetLine(int line) void GLRenderer::SetupAccelFrame() { - glBindTexture(GL_TEXTURE_2D, FramebufferTex[FrontBuffer]); + glBindTexture(GL_TEXTURE_2D, ColorBufferTex); } } diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h index 597f13e..04fb27a 100644 --- a/src/GPU3D_OpenGL.h +++ b/src/GPU3D_OpenGL.h @@ -37,8 +37,8 @@ public: virtual void RenderFrame() override; virtual u32* GetLine(int line) override; - void SetupAccelFrame(); - void PrepareCaptureFrame(); + void SetupAccelFrame() override; + void PrepareCaptureFrame() override; static std::unique_ptr<GLRenderer> New() noexcept; private: @@ -65,7 +65,7 @@ private: RendererPolygon PolygonList[2048] {}; - bool BuildRenderShader(u32 flags, const char* vs, const char* fs); + bool BuildRenderShader(u32 flags, const std::string& vs, const std::string& fs); void UseRenderShader(u32 flags); void SetupPolygon(RendererPolygon* rp, Polygon* polygon); u32* SetupVertex(Polygon* poly, int vid, Vertex* vtx, u32 vtxattr, u32* vptr); @@ -84,13 +84,13 @@ private: }; - GLuint ClearShaderPlain[3] {}; + GLuint ClearShaderPlain {}; - GLuint RenderShader[16][3] {}; + GLuint RenderShader[16] {}; GLuint CurShaderID = -1; - GLuint FinalPassEdgeShader[3] {}; - GLuint FinalPassFogShader[3] {}; + GLuint FinalPassEdgeShader {}; + GLuint FinalPassFogShader {}; // std140 compliant structure struct @@ -143,11 +143,11 @@ private: bool BetterPolygons {}; int ScreenW {}, ScreenH {}; - GLuint FramebufferTex[8] {}; - int FrontBuffer {}; - GLuint FramebufferID[4] {}, PixelbufferID {}; - u32 Framebuffer[256*192] {}; - + GLuint ColorBufferTex {}, DepthBufferTex {}, AttrBufferTex {}; + GLuint DownScaleBufferTex {}; + GLuint PixelbufferID {}; + GLuint MainFramebuffer {}, DownscaleFramebuffer {}; + u32 Framebuffer[256*192] {}; }; -} \ No newline at end of file +} diff --git a/src/GPU3D_Texcache.cpp b/src/GPU3D_Texcache.cpp new file mode 100644 index 0000000..1d409c2 --- /dev/null +++ b/src/GPU3D_Texcache.cpp @@ -0,0 +1,269 @@ +#include "GPU3D_Texcache.h" + +namespace GPU3D +{ + +inline u16 ColorAvg(u16 color0, u16 color1) +{ + u32 r0 = color0 & 0x001F; + u32 g0 = color0 & 0x03E0; + u32 b0 = color0 & 0x7C00; + u32 r1 = color1 & 0x001F; + u32 g1 = color1 & 0x03E0; + u32 b1 = color1 & 0x7C00; + + u32 r = (r0 + r1) >> 1; + u32 g = ((g0 + g1) >> 1) & 0x03E0; + u32 b = ((b0 + b1) >> 1) & 0x7C00; + + return r | g | b; +} + +inline u16 Color5of3(u16 color0, u16 color1) +{ + u32 r0 = color0 & 0x001F; + u32 g0 = color0 & 0x03E0; + u32 b0 = color0 & 0x7C00; + u32 r1 = color1 & 0x001F; + u32 g1 = color1 & 0x03E0; + u32 b1 = color1 & 0x7C00; + + u32 r = (r0*5 + r1*3) >> 3; + u32 g = ((g0*5 + g1*3) >> 3) & 0x03E0; + u32 b = ((b0*5 + b1*3) >> 3) & 0x7C00; + + return r | g | b; +} + +inline u16 Color3of5(u16 color0, u16 color1) +{ + u32 r0 = color0 & 0x001F; + u32 g0 = color0 & 0x03E0; + u32 b0 = color0 & 0x7C00; + u32 r1 = color1 & 0x001F; + u32 g1 = color1 & 0x03E0; + u32 b1 = color1 & 0x7C00; + + u32 r = (r0*3 + r1*5) >> 3; + u32 g = ((g0*3 + g1*5) >> 3) & 0x03E0; + u32 b = ((b0*3 + b1*5) >> 3) & 0x7C00; + + return r | g | b; +} + +inline u32 ConvertRGB5ToRGB8(u16 val) +{ + return (((u32)val & 0x1F) << 3) + | (((u32)val & 0x3E0) << 6) + | (((u32)val & 0x7C00) << 9); +} +inline u32 ConvertRGB5ToBGR8(u16 val) +{ + return (((u32)val & 0x1F) << 9) + | (((u32)val & 0x3E0) << 6) + | (((u32)val & 0x7C00) << 3); +} +inline u32 ConvertRGB5ToRGB6(u16 val) +{ + u8 r = (val & 0x1F) << 1; + u8 g = (val & 0x3E0) >> 4; + u8 b = (val & 0x7C00) >> 9; + if (r) r++; + if (g) g++; + if (b) b++; + return (u32)r | ((u32)g << 8) | ((u32)b << 16); +} + +template <int outputFmt> +void ConvertBitmapTexture(u32 width, u32 height, u32* output, u8* texData) +{ + for (u32 i = 0; i < width*height; i++) + { + u16 value = *(u16*)&texData[i * 2]; + + switch (outputFmt) + { + case outputFmt_RGB6A5: + output[i] = ConvertRGB5ToRGB6(value) | (value & 0x8000 ? 0x1F000000 : 0); + break; + case outputFmt_RGBA8: + output[i] = ConvertRGB5ToRGB8(value) | (value & 0x8000 ? 0xFF000000 : 0); + break; + case outputFmt_BGRA8: + output[i] = ConvertRGB5ToBGR8(value) | (value & 0x8000 ? 0xFF000000 : 0); + break; + } + } +} + +template void ConvertBitmapTexture<outputFmt_RGB6A5>(u32 width, u32 height, u32* output, u8* texData); + +template <int outputFmt> +void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u8* texAuxData, u16* palData) +{ + // we process a whole block at the time + for (int y = 0; y < height / 4; y++) + { + for (int x = 0; x < width / 4; x++) + { + u32 data = ((u32*)texData)[x + y * (width / 4)]; + u16 auxData = ((u16*)texAuxData)[x + y * (width / 4)]; + + u32 paletteOffset = auxData & 0x3FFF; + u16 color0 = palData[paletteOffset*2] | 0x8000; + u16 color1 = palData[paletteOffset*2+1] | 0x8000; + u16 color2, color3; + + switch ((auxData >> 14) & 0x3) + { + case 0: + color2 = palData[paletteOffset*2+2] | 0x8000; + color3 = 0; + break; + case 1: + { + u32 r0 = color0 & 0x001F; + u32 g0 = color0 & 0x03E0; + u32 b0 = color0 & 0x7C00; + u32 r1 = color1 & 0x001F; + u32 g1 = color1 & 0x03E0; + u32 b1 = color1 & 0x7C00; + + u32 r = (r0 + r1) >> 1; + u32 g = ((g0 + g1) >> 1) & 0x03E0; + u32 b = ((b0 + b1) >> 1) & 0x7C00; + color2 = r | g | b | 0x8000; + } + color3 = 0; + break; + case 2: + color2 = palData[paletteOffset*2+2] | 0x8000; + color3 = palData[paletteOffset*2+3] | 0x8000; + break; + case 3: + { + u32 r0 = color0 & 0x001F; + u32 g0 = color0 & 0x03E0; + u32 b0 = color0 & 0x7C00; + u32 r1 = color1 & 0x001F; + u32 g1 = color1 & 0x03E0; + u32 b1 = color1 & 0x7C00; + + u32 r = (r0*5 + r1*3) >> 3; + u32 g = ((g0*5 + g1*3) >> 3) & 0x03E0; + u32 b = ((b0*5 + b1*3) >> 3) & 0x7C00; + + color2 = r | g | b | 0x8000; + } + { + u32 r0 = color0 & 0x001F; + u32 g0 = color0 & 0x03E0; + u32 b0 = color0 & 0x7C00; + u32 r1 = color1 & 0x001F; + u32 g1 = color1 & 0x03E0; + u32 b1 = color1 & 0x7C00; + + u32 r = (r0*3 + r1*5) >> 3; + u32 g = ((g0*3 + g1*5) >> 3) & 0x03E0; + u32 b = ((b0*3 + b1*5) >> 3) & 0x7C00; + + color3 = r | g | b | 0x8000; + } + break; + } + + // in 2020 our default data types are big enough to be used as lookup tables... + u64 packed = color0 | ((u64)color1 << 16) | ((u64)color2 << 32) | ((u64)color3 << 48); + + for (int j = 0; j < 4; j++) + { + for (int i = 0; i < 4; i++) + { + u16 color = (packed >> 16 * (data >> 2 * (i + j * 4))) & 0xFFFF; + u32 res; + switch (outputFmt) + { + case outputFmt_RGB6A5: res = ConvertRGB5ToRGB6(color) + | ((color & 0x8000) ? 0x1F000000 : 0); break; + case outputFmt_RGBA8: res = ConvertRGB5ToRGB8(color) + | ((color & 0x8000) ? 0xFF000000 : 0); break; + case outputFmt_BGRA8: res = ConvertRGB5ToBGR8(color) + | ((color & 0x8000) ? 0xFF000000 : 0); break; + } + output[x * 4 + i + (y * 4 + j) * width] = res; + } + } + } + } +} + +template void ConvertCompressedTexture<outputFmt_RGB6A5>(u32, u32, u32*, u8*, u8*, u16*); + +template <int outputFmt, int X, int Y> +void ConvertAXIYTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData) +{ + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + u8 val = texData[x + y * width]; + + u32 idx = val & ((1 << Y) - 1); + + u16 color = palData[idx]; + u32 alpha = (val >> Y) & ((1 << X) - 1); + if (X != 5) + alpha = alpha * 4 + alpha / 2; + + u32 res; + switch (outputFmt) + { + case outputFmt_RGB6A5: res = ConvertRGB5ToRGB6(color) | alpha << 24; break; + // make sure full alpha == 255 + case outputFmt_RGBA8: res = ConvertRGB5ToRGB8(color) | (alpha << 27 | (alpha & 0x1C) << 22); break; + case outputFmt_BGRA8: res = ConvertRGB5ToBGR8(color) | (alpha << 27 | (alpha & 0x1C) << 22); break; + } + output[x + y * width] = res; + } + } +} + +template void ConvertAXIYTexture<outputFmt_RGB6A5, 5, 3>(u32, u32, u32*, u8*, u16*); +template void ConvertAXIYTexture<outputFmt_RGB6A5, 3, 5>(u32, u32, u32*, u8*, u16*); + +template <int outputFmt, int colorBits> +void ConvertNColorsTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData, bool color0Transparent) +{ + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width / (8 / colorBits); x++) + { + u8 val = texData[x + y * (width / (8 / colorBits))]; + + for (int i = 0; i < 8 / colorBits; i++) + { + u32 index = (val >> (i * colorBits)) & ((1 << colorBits) - 1); + u16 color = palData[index]; + + bool transparent = color0Transparent && index == 0; + u32 res; + switch (outputFmt) + { + case outputFmt_RGB6A5: res = ConvertRGB5ToRGB6(color) + | (transparent ? 0 : 0x1F000000); break; + case outputFmt_RGBA8: res = ConvertRGB5ToRGB8(color) + | (transparent ? 0 : 0xFF000000); break; + case outputFmt_BGRA8: res = ConvertRGB5ToBGR8(color) + | (transparent ? 0 : 0xFF000000); break; + } + output[x * (8 / colorBits) + y * width + i] = res; + } + } + } +} + +template void ConvertNColorsTexture<outputFmt_RGB6A5, 2>(u32, u32, u32*, u8*, u16*, bool); +template void ConvertNColorsTexture<outputFmt_RGB6A5, 4>(u32, u32, u32*, u8*, u16*, bool); +template void ConvertNColorsTexture<outputFmt_RGB6A5, 8>(u32, u32, u32*, u8*, u16*, bool); + +} \ No newline at end of file diff --git a/src/GPU3D_Texcache.h b/src/GPU3D_Texcache.h new file mode 100644 index 0000000..73d70cb --- /dev/null +++ b/src/GPU3D_Texcache.h @@ -0,0 +1,309 @@ +#ifndef GPU3D_TEXCACHE +#define GPU3D_TEXCACHE + +#include "types.h" +#include "GPU.h" + +#include <assert.h> +#include <unordered_map> + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash/xxhash.h" + +namespace GPU3D +{ + +inline u32 TextureWidth(u32 texparam) +{ + return 8 << ((texparam >> 20) & 0x7); +} + +inline u32 TextureHeight(u32 texparam) +{ + return 8 << ((texparam >> 23) & 0x7); +} + +enum +{ + outputFmt_RGB6A5, + outputFmt_RGBA8, + outputFmt_BGRA8 +}; + +template <int outputFmt> +void ConvertBitmapTexture(u32 width, u32 height, u32* output, u8* texData); +template <int outputFmt> +void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u8* texAuxData, u16* palData); +template <int outputFmt, int X, int Y> +void ConvertAXIYTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData); +template <int outputFmt, int colorBits> +void ConvertNColorsTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData, bool color0Transparent); + +template <typename TexLoaderT, typename TexHandleT> +class Texcache +{ +public: + Texcache(const TexLoaderT& texloader) + : TexLoader(texloader) // probably better if this would be a move constructor??? + {} + + bool Update() + { + auto textureDirty = GPU::VRAMDirty_Texture.DeriveState(GPU::VRAMMap_Texture); + auto texPalDirty = GPU::VRAMDirty_TexPal.DeriveState(GPU::VRAMMap_TexPal); + + bool textureChanged = GPU::MakeVRAMFlat_TextureCoherent(textureDirty); + bool texPalChanged = GPU::MakeVRAMFlat_TexPalCoherent(texPalDirty); + + if (textureChanged || texPalChanged) + { + //printf("check invalidation %d\n", TexCache.size()); + for (auto it = Cache.begin(); it != Cache.end();) + { + TexCacheEntry& entry = it->second; + if (textureChanged) + { + for (u32 i = 0; i < 2; i++) + { + u32 startBit = entry.TextureRAMStart[i] / GPU::VRAMDirtyGranularity; + u32 bitsCount = ((entry.TextureRAMStart[i] + entry.TextureRAMSize[i] + GPU::VRAMDirtyGranularity - 1) / GPU::VRAMDirtyGranularity) - startBit; + + u32 startEntry = startBit >> 6; + u64 entriesCount = ((startBit + bitsCount + 0x3F) >> 6) - startEntry; + for (u32 j = startEntry; j < startEntry + entriesCount; j++) + { + if (GetRangedBitMask(j, startBit, bitsCount) & textureDirty.Data[j]) + { + u64 newTexHash = XXH3_64bits(&GPU::VRAMFlat_Texture[entry.TextureRAMStart[i]], entry.TextureRAMSize[i]); + + if (newTexHash != entry.TextureHash[i]) + goto invalidate; + } + } + } + } + + if (texPalChanged && entry.TexPalSize > 0) + { + u32 startBit = entry.TexPalStart / GPU::VRAMDirtyGranularity; + u32 bitsCount = ((entry.TexPalStart + entry.TexPalSize + GPU::VRAMDirtyGranularity - 1) / GPU::VRAMDirtyGranularity) - startBit; + + u32 startEntry = startBit >> 6; + u64 entriesCount = ((startBit + bitsCount + 0x3F) >> 6) - startEntry; + for (u32 j = startEntry; j < startEntry + entriesCount; j++) + { + if (GetRangedBitMask(j, startBit, bitsCount) & texPalDirty.Data[j]) + { + u64 newPalHash = XXH3_64bits(&GPU::VRAMFlat_TexPal[entry.TexPalStart], entry.TexPalSize); + if (newPalHash != entry.TexPalHash) + goto invalidate; + } + } + } + + it++; + continue; + invalidate: + FreeTextures[entry.WidthLog2][entry.HeightLog2].push_back(entry.Texture); + + //printf("invalidating texture %d\n", entry.ImageDescriptor); + + it = Cache.erase(it); + } + + return true; + } + + return false; + } + + void GetTexture(u32 texParam, u32 palBase, TexHandleT& textureHandle, u32& layer, u32*& helper) + { + // remove sampling and texcoord gen params + texParam &= ~0xC00F0000; + + u32 fmt = (texParam >> 26) & 0x7; + u64 key = texParam; + if (fmt != 7) + { + key |= (u64)palBase << 32; + if (fmt == 5) + key &= ~((u64)1 << 29); + } + //printf("%" PRIx64 " %" PRIx32 " %" PRIx32 "\n", key, texParam, palBase); + + assert(fmt != 0 && "no texture is not a texture format!"); + + auto it = Cache.find(key); + + if (it != Cache.end()) + { + textureHandle = it->second.Texture.TextureID; + layer = it->second.Texture.Layer; + helper = &it->second.LastVariant; + return; + } + + u32 widthLog2 = (texParam >> 20) & 0x7; + u32 heightLog2 = (texParam >> 23) & 0x7; + u32 width = 8 << widthLog2; + u32 height = 8 << heightLog2; + + u32 addr = (texParam & 0xFFFF) * 8; + + TexCacheEntry entry = {0}; + + entry.TextureRAMStart[0] = addr; + entry.WidthLog2 = widthLog2; + entry.HeightLog2 = heightLog2; + + // apparently a new texture + if (fmt == 7) + { + entry.TextureRAMSize[0] = width*height*2; + + ConvertBitmapTexture<outputFmt_RGB6A5>(width, height, DecodingBuffer, &GPU::VRAMFlat_Texture[addr]); + } + else if (fmt == 5) + { + u8* texData = &GPU::VRAMFlat_Texture[addr]; + u32 slot1addr = 0x20000 + ((addr & 0x1FFFC) >> 1); + if (addr >= 0x40000) + slot1addr += 0x10000; + u8* texAuxData = &GPU::VRAMFlat_Texture[slot1addr]; + + u16* palData = (u16*)(GPU::VRAMFlat_TexPal + palBase*16); + + entry.TextureRAMSize[0] = width*height/16*4; + entry.TextureRAMStart[1] = slot1addr; + entry.TextureRAMSize[1] = width*height/16*2; + entry.TexPalStart = palBase*16; + entry.TexPalSize = 0x10000; + + ConvertCompressedTexture<outputFmt_RGB6A5>(width, height, DecodingBuffer, texData, texAuxData, palData); + } + else + { + u32 texSize, palAddr = palBase*16, numPalEntries; + switch (fmt) + { + case 1: texSize = width*height; numPalEntries = 32; break; + case 6: texSize = width*height; numPalEntries = 8; break; + case 2: texSize = width*height/4; numPalEntries = 4; palAddr >>= 1; break; + case 3: texSize = width*height/2; numPalEntries = 16; break; + case 4: texSize = width*height; numPalEntries = 256; break; + } + + palAddr &= 0x1FFFF; + + /*printf("creating texture | fmt: %d | %dx%d | %08x | %08x\n", fmt, width, height, addr, palAddr); + svcSleepThread(1000*1000);*/ + + entry.TextureRAMSize[0] = texSize; + entry.TexPalStart = palAddr; + entry.TexPalSize = numPalEntries*2; + + u8* texData = &GPU::VRAMFlat_Texture[addr]; + u16* palData = (u16*)(GPU::VRAMFlat_TexPal + palAddr); + + //assert(entry.TexPalStart+entry.TexPalSize <= 128*1024*1024); + + bool color0Transparent = texParam & (1 << 29); + + switch (fmt) + { + case 1: ConvertAXIYTexture<outputFmt_RGB6A5, 3, 5>(width, height, DecodingBuffer, texData, palData); break; + case 6: ConvertAXIYTexture<outputFmt_RGB6A5, 5, 3>(width, height, DecodingBuffer, texData, palData); break; + case 2: ConvertNColorsTexture<outputFmt_RGB6A5, 2>(width, height, DecodingBuffer, texData, palData, color0Transparent); break; + case 3: ConvertNColorsTexture<outputFmt_RGB6A5, 4>(width, height, DecodingBuffer, texData, palData, color0Transparent); break; + case 4: ConvertNColorsTexture<outputFmt_RGB6A5, 8>(width, height, DecodingBuffer, texData, palData, color0Transparent); break; + } + } + + for (int i = 0; i < 2; i++) + { + if (entry.TextureRAMSize[i]) + entry.TextureHash[i] = XXH3_64bits(&GPU::VRAMFlat_Texture[entry.TextureRAMStart[i]], entry.TextureRAMSize[i]); + } + if (entry.TexPalSize) + entry.TexPalHash = XXH3_64bits(&GPU::VRAMFlat_TexPal[entry.TexPalStart], entry.TexPalSize); + + auto& texArrays = TexArrays[widthLog2][heightLog2]; + auto& freeTextures = FreeTextures[widthLog2][heightLog2]; + + if (freeTextures.size() == 0) + { + texArrays.resize(texArrays.size()+1); + GLuint& array = texArrays[texArrays.size()-1]; + + u32 layers = std::min<u32>((8*1024*1024) / (width*height*4), 64); + + // allocate new array texture + //printf("allocating new layer set for %d %d %d %d\n", width, height, texArrays.size()-1, array.ImageDescriptor); + array = TexLoader.GenerateTexture(width, height, layers); + + for (u32 i = 0; i < layers; i++) + { + freeTextures.push_back(TexArrayEntry{array, i}); + } + } + + TexArrayEntry storagePlace = freeTextures[freeTextures.size()-1]; + freeTextures.pop_back(); + + entry.Texture = storagePlace; + + TexLoader.UploadTexture(storagePlace.TextureID, width, height, storagePlace.Layer, DecodingBuffer); + //printf("using storage place %d %d | %d %d (%d)\n", width, height, storagePlace.TexArrayIdx, storagePlace.LayerIdx, array.ImageDescriptor); + + textureHandle = storagePlace.TextureID; + layer = storagePlace.Layer; + helper = &Cache.emplace(std::make_pair(key, entry)).first->second.LastVariant; + } + + void Reset() + { + for (u32 i = 0; i < 8; i++) + { + for (u32 j = 0; j < 8; j++) + { + for (u32 k = 0; k < TexArrays[i][j].size(); k++) + TexLoader.DeleteTexture(TexArrays[i][j][k]); + TexArrays[i][j].clear(); + FreeTextures[i][j].clear(); + } + } + Cache.clear(); + } +private: + struct TexArrayEntry + { + TexHandleT TextureID; + u32 Layer; + }; + + struct TexCacheEntry + { + u32 LastVariant; // very cheap way to make variant lookup faster + + u32 TextureRAMStart[2], TextureRAMSize[2]; + u32 TexPalStart, TexPalSize; + u8 WidthLog2, HeightLog2; + TexArrayEntry Texture; + + u64 TextureHash[2]; + u64 TexPalHash; + }; + std::unordered_map<u64, TexCacheEntry> Cache; + + TexLoaderT TexLoader; + + std::vector<TexArrayEntry> FreeTextures[8][8]; + std::vector<TexHandleT> TexArrays[8][8]; + + u32 DecodingBuffer[1024*1024]; +}; + +} + +#endif \ No newline at end of file diff --git a/src/GPU3D_TexcacheOpenGL.cpp b/src/GPU3D_TexcacheOpenGL.cpp new file mode 100644 index 0000000..0473237 --- /dev/null +++ b/src/GPU3D_TexcacheOpenGL.cpp @@ -0,0 +1,29 @@ +#include "GPU3D_TexcacheOpenGL.h" + +namespace GPU3D +{ + +GLuint TexcacheOpenGLLoader::GenerateTexture(u32 width, u32 height, u32 layers) +{ + GLuint texarray; + glGenTextures(1, &texarray); + glBindTexture(GL_TEXTURE_2D_ARRAY, texarray); + glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8UI, width, height, layers); + return texarray; +} + +void TexcacheOpenGLLoader::UploadTexture(GLuint handle, u32 width, u32 height, u32 layer, void* data) +{ + glBindTexture(GL_TEXTURE_2D_ARRAY, handle); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, + 0, 0, 0, layer, + width, height, 1, + GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, data); +} + +void TexcacheOpenGLLoader::DeleteTexture(GLuint handle) +{ + glDeleteTextures(1, &handle); +} + +} \ No newline at end of file diff --git a/src/GPU3D_TexcacheOpenGL.h b/src/GPU3D_TexcacheOpenGL.h new file mode 100644 index 0000000..d61ae24 --- /dev/null +++ b/src/GPU3D_TexcacheOpenGL.h @@ -0,0 +1,25 @@ +#ifndef GPU3D_TEXCACHEOPENGL +#define GPU3D_TEXCACHEOPENGL + +#include "GPU3D_Texcache.h" +#include "OpenGLSupport.h" + +namespace GPU3D +{ + +template <typename, typename> +class Texcache; + +class TexcacheOpenGLLoader +{ +public: + GLuint GenerateTexture(u32 width, u32 height, u32 layers); + void UploadTexture(GLuint handle, u32 width, u32 height, u32 layer, void* data); + void DeleteTexture(GLuint handle); +}; + +using TexcacheOpenGL = Texcache<TexcacheOpenGLLoader, GLuint>; + +} + +#endif \ No newline at end of file diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp index 47e04d2..9d81717 100644 --- a/src/GPU_OpenGL.cpp +++ b/src/GPU_OpenGL.cpp @@ -36,32 +36,27 @@ using namespace OpenGL; std::unique_ptr<GLCompositor> GLCompositor::New() noexcept { assert(glBindAttribLocation != nullptr); + GLuint CompShader {}; - std::array<GLuint, 3> CompShader {}; - if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, &CompShader[0], "CompositorShader")) - return nullptr; - - glBindAttribLocation(CompShader[2], 0, "vPosition"); - glBindAttribLocation(CompShader[2], 1, "vTexcoord"); - glBindFragDataLocation(CompShader[2], 0, "oColor"); - - if (!OpenGL::LinkShaderProgram(CompShader.data())) - // OpenGL::LinkShaderProgram already deletes the shader program object - // if linking the shaders together failed. + if (!OpenGL::CompileVertexFragmentProgram(CompShader, + kCompositorVS, kCompositorFS_Nearest, + "CompositorShader", + {{"vPosition", 0}, {"vTexcoord", 1}}, + {{"oColor", 0}})) return nullptr; return std::unique_ptr<GLCompositor>(new GLCompositor(CompShader)); } -GLCompositor::GLCompositor(std::array<GLuint, 3> compShader) noexcept : CompShader(compShader) +GLCompositor::GLCompositor(GLuint compShader) noexcept : CompShader(compShader) { - CompScaleLoc = glGetUniformLocation(CompShader[2], "u3DScale"); - Comp3DXPosLoc = glGetUniformLocation(CompShader[2], "u3DXPos"); + CompScaleLoc = glGetUniformLocation(CompShader, "u3DScale"); + Comp3DXPosLoc = glGetUniformLocation(CompShader, "u3DXPos"); - glUseProgram(CompShader[2]); - GLuint screenTextureUniform = glGetUniformLocation(CompShader[2], "ScreenTex"); + glUseProgram(CompShader); + GLuint screenTextureUniform = glGetUniformLocation(CompShader, "ScreenTex"); glUniform1i(screenTextureUniform, 0); - GLuint _3dTextureUniform = glGetUniformLocation(CompShader[2], "_3DTex"); + GLuint _3dTextureUniform = glGetUniformLocation(CompShader, "_3DTex"); glUniform1i(_3dTextureUniform, 1); // all this mess is to prevent bleeding @@ -136,7 +131,7 @@ GLCompositor::~GLCompositor() glDeleteVertexArrays(1, &CompVertexArrayID); glDeleteBuffers(1, &CompVertexBufferID); - OpenGL::DeleteShaderProgram(CompShader.data()); + glDeleteProgram(CompShader); } void GLCompositor::Reset() @@ -186,9 +181,9 @@ void GLCompositor::Stop() void GLCompositor::RenderFrame() { - int frontbuf = GPU::FrontBuffer; + int backbuf = GPU::FrontBuffer ^ 1; glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[backbuf]); glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); @@ -200,7 +195,7 @@ void GLCompositor::RenderFrame() glClear(GL_COLOR_BUFFER_BIT); // TODO: select more shaders (filtering, etc) - OpenGL::UseShaderProgram(CompShader.data()); + glUseProgram(CompShader); glUniform1ui(CompScaleLoc, Scale); // TODO: support setting this midframe, if ever needed @@ -209,16 +204,16 @@ void GLCompositor::RenderFrame() glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); - if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) + if (GPU::Framebuffer[backbuf][0] && GPU::Framebuffer[backbuf][1]) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); + GL_UNSIGNED_BYTE, GPU::Framebuffer[backbuf][0]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); + GL_UNSIGNED_BYTE, GPU::Framebuffer[backbuf][1]); } glActiveTexture(GL_TEXTURE1); - reinterpret_cast<GPU3D::GLRenderer*>(GPU3D::CurrentRenderer.get())->SetupAccelFrame(); + GPU3D::CurrentRenderer->SetupAccelFrame(); glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); glBindVertexArray(CompVertexArrayID); diff --git a/src/GPU_OpenGL.h b/src/GPU_OpenGL.h index 90c17ae..a507842 100644 --- a/src/GPU_OpenGL.h +++ b/src/GPU_OpenGL.h @@ -44,12 +44,12 @@ public: void RenderFrame(); void BindOutputTexture(int buf); private: - GLCompositor(std::array<GLuint, 3> CompShader) noexcept; + GLCompositor(GLuint CompShader) noexcept; int Scale; int ScreenH, ScreenW; - std::array<GLuint, 3> CompShader; + GLuint CompShader; GLuint CompScaleLoc; GLuint Comp3DXPosLoc; @@ -68,4 +68,4 @@ private: GLuint CompScreenOutputFB[2]; }; -} \ No newline at end of file +} diff --git a/src/NDS.cpp b/src/NDS.cpp index 832661a..0e00bab 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -33,6 +33,7 @@ #include "AREngine.h" #include "Platform.h" #include "FreeBIOS.h" +#include "GPU3D.h" #ifdef JIT_ENABLED #include "ARMJIT.h" diff --git a/src/NonStupidBitfield.h b/src/NonStupidBitfield.h index a4fe7ec..6d9cd2c 100644 --- a/src/NonStupidBitfield.h +++ b/src/NonStupidBitfield.h @@ -26,6 +26,32 @@ #include <initializer_list> #include <algorithm> +inline u64 GetRangedBitMask(u32 idx, u32 startBit, u32 bitsCount) +{ + u32 startEntry = startBit >> 6; + u64 entriesCount = ((startBit + bitsCount + 0x3F) >> 6) - startEntry; + + if (entriesCount > 1) + { + if (idx == startEntry) + return 0xFFFFFFFFFFFFFFFF << (startBit & 0x3F); + if (((startBit + bitsCount) & 0x3F) && idx == startEntry + entriesCount - 1) + return ~(0xFFFFFFFFFFFFFFFF << ((startBit + bitsCount) & 0x3F)); + + return 0xFFFFFFFFFFFFFFFF; + } + else if (idx == startEntry) + { + return bitsCount == 64 + ? 0xFFFFFFFFFFFFFFFF + : ((1ULL << bitsCount) - 1) << (startBit & 0x3F); + } + else + { + return 0; + } +} + // like std::bitset but less stupid and optimised for // our use case (keeping track of memory invalidations) @@ -164,6 +190,11 @@ struct NonStupidBitField return Ref{*this, idx}; } + bool operator[](u32 idx) const + { + return Data[idx >> 6] & (1ULL << (idx & 0x3F)); + } + void SetRange(u32 startBit, u32 bitsCount) { u32 startEntry = startBit >> 6; @@ -185,6 +216,26 @@ struct NonStupidBitField } } + int Min() const + { + for (int i = 0; i < DataLength; i++) + { + if (Data[i]) + return i * 64 + __builtin_ctzll(Data[i]); + } + return -1; + } + + int Max() const + { + for (int i = DataLength - 1; i >= 0; i--) + { + if (Data[i]) + return i * 64 + (63 - __builtin_clzll(Data[i])); + } + return -1; + } + NonStupidBitField& operator|=(const NonStupidBitField<Size>& other) { for (u32 i = 0; i < DataLength; i++) @@ -193,6 +244,7 @@ struct NonStupidBitField } return *this; } + NonStupidBitField& operator&=(const NonStupidBitField<Size>& other) { for (u32 i = 0; i < DataLength; i++) @@ -201,6 +253,20 @@ struct NonStupidBitField } return *this; } + + operator bool() const + { + for (int i = 0; i < DataLength - 1; i++) + { + if (Data[i]) + return true; + } + if (Data[DataLength-1] & ((Size&0x3F) ? ~(0xFFFFFFFFFFFFFFFF << (Size&0x3F)) : 0xFFFFFFFFFFFFFFFF)) + { + return true; + } + return false; + } }; diff --git a/src/OpenGLSupport.cpp b/src/OpenGLSupport.cpp index 5a8da11..0c793fa 100644 --- a/src/OpenGLSupport.cpp +++ b/src/OpenGLSupport.cpp @@ -18,78 +18,204 @@ #include "OpenGLSupport.h" +#include <unordered_map> +#include <vector> + +#include <assert.h> + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash/xxhash.h" + using Platform::Log; using Platform::LogLevel; namespace OpenGL { -bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name) +struct ShaderCacheEntry { - int len; - int res; + u32 Length; + u8* Data; + u32 BinaryFormat; - if (!glCreateShader) + ShaderCacheEntry(u8* data, u32 length, u32 binaryFmt) + : Length(length), Data(data), BinaryFormat(binaryFmt) { - Log(LogLevel::Error, "OpenGL: Cannot build shader program, OpenGL hasn't been loaded\n"); - return false; + assert(data != nullptr); } - ids[0] = glCreateShader(GL_VERTEX_SHADER); - len = strlen(vs); - glShaderSource(ids[0], 1, &vs, &len); - glCompileShader(ids[0]); + ShaderCacheEntry(const ShaderCacheEntry&) = delete; + ShaderCacheEntry(ShaderCacheEntry&& other) + { + Data = other.Data; + Length = other.Length; + BinaryFormat = other.BinaryFormat; - glGetShaderiv(ids[0], GL_COMPILE_STATUS, &res); - if (res != GL_TRUE) + other.Data = nullptr; + other.Length = 0; + other.BinaryFormat = 0; + } + + ~ShaderCacheEntry() { - glGetShaderiv(ids[0], GL_INFO_LOG_LENGTH, &res); - if (res < 1) res = 1024; - char* log = new char[res+1]; - glGetShaderInfoLog(ids[0], res+1, NULL, log); - Log(LogLevel::Error, "OpenGL: failed to compile vertex shader %s: %s\n", name, log); - Log(LogLevel::Debug, "shader source:\n--\n%s\n--\n", vs); - delete[] log; + if (Data) // check whether it was moved + delete[] Data; + } +}; + +std::unordered_map<u64, ShaderCacheEntry> ShaderCache; +std::vector<u64> NewShaders; + +constexpr u32 ShaderCacheMagic = 0x11CAC4E1; +constexpr u32 ShaderCacheVersion = 1; + +void LoadShaderCache() +{ + // for now the shader cache only contains only compute shaders + // because they take the longest to compile + Platform::FileHandle* file = Platform::OpenLocalFile("shadercache", Platform::FileMode::Read); + if (file == nullptr) + { + Log(LogLevel::Error, "Could not find shader cache\n"); + return; + } + + u32 magic, version, numPrograms; + if (Platform::FileRead(&magic, 4, 1, file) != 1 || magic != ShaderCacheMagic) + { + Log(LogLevel::Error, "Shader cache file has invalid magic\n"); + goto fileInvalid; + } + + if (Platform::FileRead(&version, 4, 1, file) != 1 || version != ShaderCacheVersion) + { + Log(LogLevel::Error, "Shader cache file has bad version\n"); + goto fileInvalid; + } + + if (Platform::FileRead(&numPrograms, 4, 1, file) != 1) + { + Log(LogLevel::Error, "Shader cache file invalid program count\n"); + goto fileInvalid; + } + + // not the best approach, because once changes pile up + // we read and overwrite the old files + for (u32 i = 0; i < numPrograms; i++) + { + int error = 3; + + u32 length, binaryFormat; + u64 sourceHash; + error -= Platform::FileRead(&sourceHash, 8, 1, file); + error -= Platform::FileRead(&length, 4, 1, file); + error -= Platform::FileRead(&binaryFormat, 4, 1, file); + + if (error != 0) + { + Log(LogLevel::Error, "Invalid shader cache entry\n"); + goto fileInvalid; + } + + u8* data = new u8[length]; + if (Platform::FileRead(data, length, 1, file) != 1) + { + Log(LogLevel::Error, "Could not read shader cache entry data\n"); + delete[] data; + goto fileInvalid; + } + + ShaderCache.erase(sourceHash); + ShaderCache.emplace(sourceHash, ShaderCacheEntry(data, length, binaryFormat)); + } + +fileInvalid: + Platform::CloseFile(file); +} + +void SaveShaderCache() +{ + Platform::FileHandle* file = Platform::OpenLocalFile("shadercache", Platform::FileMode::ReadWrite); + + if (file == nullptr) + { + Log(LogLevel::Error, "Could not open or create shader cache file\n"); + return; + } + + int written = 3; + u32 magic = ShaderCacheMagic, version = ShaderCacheVersion, numPrograms = ShaderCache.size(); + written -= Platform::FileWrite(&magic, 4, 1, file); + written -= Platform::FileWrite(&version, 4, 1, file); + written -= Platform::FileWrite(&numPrograms, 4, 1, file); + + if (written != 0) + { + Log(LogLevel::Error, "Could not write shader cache header\n"); + goto writeError; + } + + Platform::FileSeek(file, 0, Platform::FileSeekOrigin::End); + + printf("new shaders %d\n", NewShaders.size()); - glDeleteShader(ids[0]); + for (u64 newShader : NewShaders) + { + int error = 4; + auto it = ShaderCache.find(newShader); + + error -= Platform::FileWrite(&it->first, 8, 1, file); + error -= Platform::FileWrite(&it->second.Length, 4, 1, file); + error -= Platform::FileWrite(&it->second.BinaryFormat, 4, 1, file); + error -= Platform::FileWrite(it->second.Data, it->second.Length, 1, file); + + if (error != 0) + { + Log(LogLevel::Error, "Could not insert new shader cache entry\n"); + goto writeError; + } + } + +writeError: + Platform::CloseFile(file); + + NewShaders.clear(); +} + +bool CompilerShader(GLuint& id, const std::string& source, const std::string& name, const std::string& type) +{ + int res; + if (!glCreateShader) + { + Log(LogLevel::Error, "OpenGL: Cannot build shader program, OpenGL hasn't been loaded\n"); return false; } - ids[1] = glCreateShader(GL_FRAGMENT_SHADER); - len = strlen(fs); - glShaderSource(ids[1], 1, &fs, &len); - glCompileShader(ids[1]); + const char* sourceC = source.c_str(); + int len = source.length(); + glShaderSource(id, 1, &sourceC, &len); + + glCompileShader(id); - glGetShaderiv(ids[1], GL_COMPILE_STATUS, &res); + glGetShaderiv(id, GL_COMPILE_STATUS, &res); if (res != GL_TRUE) { - glGetShaderiv(ids[1], GL_INFO_LOG_LENGTH, &res); + glGetShaderiv(id, GL_INFO_LOG_LENGTH, &res); if (res < 1) res = 1024; char* log = new char[res+1]; - glGetShaderInfoLog(ids[1], res+1, NULL, log); - Log(LogLevel::Error, "OpenGL: failed to compile fragment shader %s: %s\n", name, log); - //printf("shader source:\n--\n%s\n--\n", fs); + glGetShaderInfoLog(id, res+1, NULL, log); + Log(LogLevel::Error, "OpenGL: failed to compile %s shader %s: %s\n", type.c_str(), name.c_str(), log); + Log(LogLevel::Debug, "shader source:\n--\n%s\n--\n", source.c_str()); delete[] log; - Platform::FileHandle* logf = Platform::OpenFile("shaderfail.log", Platform::FileMode::WriteText); - Platform::FileWrite(fs, len+1, 1, logf); - Platform::CloseFile(logf); - - glDeleteShader(ids[0]); - glDeleteShader(ids[1]); - return false; } - ids[2] = glCreateProgram(); - glAttachShader(ids[2], ids[0]); - glAttachShader(ids[2], ids[1]); - return true; } -bool LinkShaderProgram(GLuint* ids) +bool LinkProgram(GLuint& result, GLuint* ids, int numIds) { int res; @@ -99,46 +225,132 @@ bool LinkShaderProgram(GLuint* ids) return false; } - glLinkProgram(ids[2]); + for (int i = 0; i < numIds; i++) + { + glAttachShader(result, ids[i]); + } - glDetachShader(ids[2], ids[0]); - glDetachShader(ids[2], ids[1]); + glLinkProgram(result); - glDeleteShader(ids[0]); - glDeleteShader(ids[1]); + for (int i = 0; i < numIds; i++) + glDetachShader(result, ids[i]); - glGetProgramiv(ids[2], GL_LINK_STATUS, &res); + glGetProgramiv(result, GL_LINK_STATUS, &res); if (res != GL_TRUE) { - glGetProgramiv(ids[2], GL_INFO_LOG_LENGTH, &res); + glGetProgramiv(result, GL_INFO_LOG_LENGTH, &res); if (res < 1) res = 1024; char* log = new char[res+1]; - glGetProgramInfoLog(ids[2], res+1, NULL, log); + glGetProgramInfoLog(result, res+1, NULL, log); Log(LogLevel::Error, "OpenGL: failed to link shader program: %s\n", log); delete[] log; - glDeleteProgram(ids[2]); - return false; } return true; } -void DeleteShaderProgram(GLuint* ids) +bool CompileComputeProgram(GLuint& result, const std::string& source, const std::string& name) { - if (glDeleteProgram) - { // If OpenGL isn't loaded, then there's no shader program to delete - glDeleteProgram(ids[2]); + result = glCreateProgram(); + + /*u64 sourceHash = XXH64(source.data(), source.size(), 0); + auto it = ShaderCache.find(sourceHash); + if (it != ShaderCache.end()) + { + glProgramBinary(result, it->second.BinaryFormat, it->second.Data, it->second.Length); + + GLint linkStatus; + glGetProgramiv(result, GL_LINK_STATUS, &linkStatus); + if (linkStatus == GL_TRUE) + { + Log(LogLevel::Info, "Restored shader %s from cache\n", name.c_str()); + return true; + } + else + { + } + }*/ + Log(LogLevel::Error, "Shader %s from cache was rejected\n", name.c_str()); + + GLuint shader; + bool linkingSucess = false; + + if (!glCreateShader || !glDeleteShader) + goto error; + + shader = glCreateShader(GL_COMPUTE_SHADER); + + if (!CompilerShader(shader, source, name, "compute")) + goto error; + + linkingSucess = LinkProgram(result, &shader, 1); + +error: + glDeleteShader(shader); + + if (!linkingSucess) + { + glDeleteProgram(result); } + /*else + { + GLint length; + GLenum format; + glGetProgramiv(result, GL_PROGRAM_BINARY_LENGTH, &length); + + u8* buffer = new u8[length]; + glGetProgramBinary(result, length, nullptr, &format, buffer); + + ShaderCache.emplace(sourceHash, ShaderCacheEntry(buffer, length, format)); + NewShaders.push_back(sourceHash); + }*/ + + return linkingSucess; } -void UseShaderProgram(GLuint* ids) +bool CompileVertexFragmentProgram(GLuint& result, + const std::string& vs, const std::string& fs, + const std::string& name, + const std::initializer_list<AttributeTarget>& vertexInAttrs, + const std::initializer_list<AttributeTarget>& fragmentOutAttrs) { - if (glUseProgram) - { // If OpenGL isn't loaded, then there's no shader program to use - glUseProgram(ids[2]); + GLuint shaders[2] = + { + glCreateShader(GL_VERTEX_SHADER), + glCreateShader(GL_FRAGMENT_SHADER) + }; + result = glCreateProgram(); + + bool linkingSucess = false; + + if (!CompilerShader(shaders[0], vs, name, "vertex")) + goto error; + + if (!CompilerShader(shaders[1], fs, name, "fragment")) + goto error; + + + for (const AttributeTarget& target : vertexInAttrs) + { + glBindAttribLocation(result, target.Location, target.Name); } + for (const AttributeTarget& target : fragmentOutAttrs) + { + glBindFragDataLocation(result, target.Location, target.Name); + } + + linkingSucess = LinkProgram(result, shaders, 2); + +error: + glDeleteShader(shaders[1]); + glDeleteShader(shaders[0]); + + if (!linkingSucess) + glDeleteProgram(result); + + return linkingSucess; } } diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h index 14be01a..ec2cb1f 100644 --- a/src/OpenGLSupport.h +++ b/src/OpenGLSupport.h @@ -29,10 +29,23 @@ namespace OpenGL { -bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name); -bool LinkShaderProgram(GLuint* ids); -void DeleteShaderProgram(GLuint* ids); -void UseShaderProgram(GLuint* ids); +void LoadShaderCache(); +void SaveShaderCache(); + +struct AttributeTarget +{ + const char* Name; + u32 Location; +}; + + +bool CompileVertexFragmentProgram(GLuint& result, + const std::string& vs, const std::string& fs, + const std::string& name, + const std::initializer_list<AttributeTarget>& vertexInAttrs, + const std::initializer_list<AttributeTarget>& fragmentOutAttrs); + +bool CompileComputeProgram(GLuint& result, const std::string& source, const std::string& name); } diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index c28f63a..cc489cc 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -22,6 +22,7 @@ #include <inttypes.h> #include "Platform.h" #include "Config.h" +#include "GPU.h" namespace Config @@ -58,6 +59,7 @@ bool Threaded3D; int GL_ScaleFactor; bool GL_BetterPolygons; +bool GL_HiresCoordinates; bool LimitFPS; bool AudioSync; @@ -243,11 +245,12 @@ ConfigEntry ConfigFile[] = {"ScreenVSync", 1, &ScreenVSync, false, false}, {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, false}, - {"3DRenderer", 0, &_3DRenderer, 0, false}, + {"3DRenderer", 0, &_3DRenderer, GPU::renderer3D_Software, false}, {"Threaded3D", 1, &Threaded3D, true, false}, {"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, false}, {"GL_BetterPolygons", 1, &GL_BetterPolygons, false, false}, + {"GL_HiresCoordinates", 1, &GL_HiresCoordinates, true, false}, {"LimitFPS", 1, &LimitFPS, true, false}, {"AudioSync", 1, &AudioSync, false}, diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index fba9bfb..19bf0c4 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -103,6 +103,7 @@ extern bool Threaded3D; extern int GL_ScaleFactor; extern bool GL_BetterPolygons; +extern bool GL_HiresCoordinates; extern bool LimitFPS; extern bool AudioSync; diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp index d3becc1..6842d5f 100644 --- a/src/frontend/qt_sdl/OSD.cpp +++ b/src/frontend/qt_sdl/OSD.cpp @@ -57,7 +57,7 @@ struct Item std::deque<Item> ItemQueue; -GLuint Shader[3]; +GLuint Shader; GLint uScreenSize, uOSDPos, uOSDSize; GLfloat uScaleFactor; GLuint OSDVertexArray; @@ -70,20 +70,19 @@ bool Init(bool openGL) { if (openGL) { - OpenGL::BuildShaderProgram(kScreenVS_OSD, kScreenFS_OSD, Shader, "OSDShader"); + OpenGL::CompileVertexFragmentProgram(Shader, + kScreenVS_OSD, kScreenFS_OSD, + "OSDShader", + {{"vPosition", 0}}, + {{"oColor", 0}}); - GLuint pid = Shader[2]; - glBindAttribLocation(pid, 0, "vPosition"); - glBindFragDataLocation(pid, 0, "oColor"); + glUseProgram(Shader); + glUniform1i(glGetUniformLocation(Shader, "OSDTex"), 0); - OpenGL::LinkShaderProgram(Shader); - glUseProgram(pid); - glUniform1i(glGetUniformLocation(pid, "OSDTex"), 0); - - uScreenSize = glGetUniformLocation(pid, "uScreenSize"); - uOSDPos = glGetUniformLocation(pid, "uOSDPos"); - uOSDSize = glGetUniformLocation(pid, "uOSDSize"); - uScaleFactor = glGetUniformLocation(pid, "uScaleFactor"); + uScreenSize = glGetUniformLocation(Shader, "uScreenSize"); + uOSDPos = glGetUniformLocation(Shader, "uOSDPos"); + uOSDSize = glGetUniformLocation(Shader, "uOSDSize"); + uScaleFactor = glGetUniformLocation(Shader, "uScaleFactor"); float vertices[6*2] = { @@ -425,7 +424,7 @@ void DrawGL(float w, float h) u32 y = kOSDMargin; - glUseProgram(Shader[2]); + glUseProgram(Shader); glUniform2f(uScreenSize, w, h); glUniform1f(uScaleFactor, mainWindow->devicePixelRatioF()); diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp index 95ec7d3..5ef10c5 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.cpp +++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp @@ -23,6 +23,7 @@ #include "types.h" #include "Platform.h" #include "Config.h" +#include "GPU.h" #include "VideoSettingsDialog.h" #include "ui_VideoSettingsDialog.h" @@ -30,11 +31,20 @@ inline bool UsesGL() { - return (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + return (Config::ScreenUseGL != 0) || (Config::_3DRenderer != GPU::renderer3D_Software); } VideoSettingsDialog* VideoSettingsDialog::currentDlg = nullptr; +void VideoSettingsDialog::setEnabled() +{ + bool softwareRenderer = Config::_3DRenderer == GPU::renderer3D_Software; + ui->cbGLDisplay->setEnabled(softwareRenderer); + ui->cbSoftwareThreaded->setEnabled(softwareRenderer); + ui->cbxGLResolution->setEnabled(!softwareRenderer); + ui->cbBetterPolygons->setEnabled(Config::_3DRenderer == GPU::renderer3D_OpenGL); + ui->cbxComputeHiResCoords->setEnabled(Config::_3DRenderer == GPU::renderer3D_OpenGLCompute); +} VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::VideoSettingsDialog) { @@ -48,10 +58,12 @@ VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui( oldSoftThreaded = Config::Threaded3D; oldGLScale = Config::GL_ScaleFactor; oldGLBetterPolygons = Config::GL_BetterPolygons; + oldHiresCoordinates = Config::GL_HiresCoordinates; grp3DRenderer = new QButtonGroup(this); - grp3DRenderer->addButton(ui->rb3DSoftware, 0); - grp3DRenderer->addButton(ui->rb3DOpenGL, 1); + grp3DRenderer->addButton(ui->rb3DSoftware, GPU::renderer3D_Software); + grp3DRenderer->addButton(ui->rb3DOpenGL, GPU::renderer3D_OpenGL); + grp3DRenderer->addButton(ui->rb3DCompute, GPU::renderer3D_OpenGLCompute); #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) connect(grp3DRenderer, SIGNAL(buttonClicked(int)), this, SLOT(onChange3DRenderer(int))); #else @@ -75,25 +87,13 @@ VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui( ui->cbxGLResolution->setCurrentIndex(Config::GL_ScaleFactor-1); ui->cbBetterPolygons->setChecked(Config::GL_BetterPolygons != 0); + ui->cbxComputeHiResCoords->setChecked(Config::GL_HiresCoordinates != 0); if (!Config::ScreenVSync) ui->sbVSyncInterval->setEnabled(false); setVsyncControlEnable(UsesGL()); - if (Config::_3DRenderer == 0) - { - ui->cbGLDisplay->setEnabled(true); - ui->cbSoftwareThreaded->setEnabled(true); - ui->cbxGLResolution->setEnabled(false); - ui->cbBetterPolygons->setEnabled(false); - } - else - { - ui->cbGLDisplay->setEnabled(false); - ui->cbSoftwareThreaded->setEnabled(false); - ui->cbxGLResolution->setEnabled(true); - ui->cbBetterPolygons->setEnabled(true); - } + setEnabled(); } VideoSettingsDialog::~VideoSettingsDialog() @@ -119,6 +119,7 @@ void VideoSettingsDialog::on_VideoSettingsDialog_rejected() Config::Threaded3D = oldSoftThreaded; Config::GL_ScaleFactor = oldGLScale; Config::GL_BetterPolygons = oldGLBetterPolygons; + Config::GL_HiresCoordinates = oldHiresCoordinates; emit updateVideoSettings(old_gl != UsesGL()); @@ -133,31 +134,18 @@ void VideoSettingsDialog::setVsyncControlEnable(bool hasOGL) void VideoSettingsDialog::onChange3DRenderer(int renderer) { - bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + bool old_gl = UsesGL(); Config::_3DRenderer = renderer; - if (renderer == 0) - { - ui->cbGLDisplay->setEnabled(true); - ui->cbSoftwareThreaded->setEnabled(true); - ui->cbxGLResolution->setEnabled(false); - ui->cbBetterPolygons->setEnabled(false); - } - else - { - ui->cbGLDisplay->setEnabled(false); - ui->cbSoftwareThreaded->setEnabled(false); - ui->cbxGLResolution->setEnabled(true); - ui->cbBetterPolygons->setEnabled(true); - } + setEnabled(); emit updateVideoSettings(old_gl != UsesGL()); } void VideoSettingsDialog::on_cbGLDisplay_stateChanged(int state) { - bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + bool old_gl = UsesGL(); Config::ScreenUseGL = (state != 0); @@ -205,3 +193,10 @@ void VideoSettingsDialog::on_cbBetterPolygons_stateChanged(int state) emit updateVideoSettings(false); } + +void VideoSettingsDialog::on_cbxComputeHiResCoords_stateChanged(int state) +{ + Config::GL_HiresCoordinates = (state != 0); + + emit updateVideoSettings(false); +} diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.h b/src/frontend/qt_sdl/VideoSettingsDialog.h index 7fee5bb..166a826 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.h +++ b/src/frontend/qt_sdl/VideoSettingsDialog.h @@ -65,10 +65,12 @@ private slots: void on_cbxGLResolution_currentIndexChanged(int idx); void on_cbBetterPolygons_stateChanged(int state); + void on_cbxComputeHiResCoords_stateChanged(int state); void on_cbSoftwareThreaded_stateChanged(int state); private: void setVsyncControlEnable(bool hasOGL); + void setEnabled(); Ui::VideoSettingsDialog* ui; @@ -81,6 +83,7 @@ private: int oldSoftThreaded; int oldGLScale; int oldGLBetterPolygons; + int oldHiresCoordinates; }; #endif // VIDEOSETTINGSDIALOG_H diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.ui b/src/frontend/qt_sdl/VideoSettingsDialog.ui index 11cfe3d..ff9baf8 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.ui +++ b/src/frontend/qt_sdl/VideoSettingsDialog.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>408</width> + <width>427</width> <height>262</height> </rect> </property> @@ -24,7 +24,7 @@ <enum>QLayout::SetFixedSize</enum> </property> <property name="horizontalSpacing"> - <number>-1</number> + <number>6</number> </property> <item row="1" column="1"> <widget class="QGroupBox" name="groupBox_3"> @@ -39,6 +39,16 @@ </property> </widget> </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="cbBetterPolygons"> + <property name="whatsThis"> + <string><html><head/><body><p>Enabling this may help reduce distortion on quads and more complex polygons, but may also reduce performance.</p></body></html></string> + </property> + <property name="text"> + <string>Improved polygon splitting</string> + </property> + </widget> + </item> <item row="1" column="0"> <widget class="QComboBox" name="cbxGLResolution"> <property name="whatsThis"> @@ -46,13 +56,10 @@ </property> </widget> </item> - <item row="2" column="0"> - <widget class="QCheckBox" name="cbBetterPolygons"> - <property name="whatsThis"> - <string><html><head/><body><p>Enabling this may help reduce distortion on quads and more complex polygons, but may also reduce performance.</p></body></html></string> - </property> + <item row="3" column="0"> + <widget class="QCheckBox" name="cbxComputeHiResCoords"> <property name="text"> - <string>Improved polygon splitting</string> + <string>Use high resolution coordinates</string> </property> </widget> </item> @@ -94,23 +101,7 @@ <string>Display settings</string> </property> <layout class="QGridLayout" name="gridLayout_2"> - <item row="6" column="0"> - <widget class="QLabel" name="label_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="whatsThis"> - <string><html><head/><body><p>The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...</p></body></html></string> - </property> - <property name="text"> - <string>VSync interval:</string> - </property> - </widget> - </item> - <item row="6" column="1"> + <item row="7" column="1"> <widget class="QSpinBox" name="sbVSyncInterval"> <property name="whatsThis"> <string><html><head/><body><p>The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...</p></body></html></string> @@ -123,7 +114,7 @@ </property> </widget> </item> - <item row="4" column="0" colspan="2"> + <item row="5" column="0" colspan="2"> <widget class="QCheckBox" name="cbGLDisplay"> <property name="whatsThis"> <string><html><head/><body><p>Use OpenGL to draw the DS screens to the main window. May result in better frame pacing. Mandatory when using the OpenGL 3D renderer.</p></body></html></string> @@ -133,17 +124,7 @@ </property> </widget> </item> - <item row="5" column="0" colspan="2"> - <widget class="QCheckBox" name="cbVSync"> - <property name="whatsThis"> - <string><html><head/><body><p>When using OpenGL, synchronize the video output to your monitor's refresh rate.</p></body></html></string> - </property> - <property name="text"> - <string>VSync</string> - </property> - </widget> - </item> - <item row="3" column="0" colspan="2"> + <item row="4" column="0" colspan="2"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -159,13 +140,39 @@ </property> </spacer> </item> + <item row="6" column="0" colspan="2"> + <widget class="QCheckBox" name="cbVSync"> + <property name="whatsThis"> + <string><html><head/><body><p>When using OpenGL, synchronize the video output to your monitor's refresh rate.</p></body></html></string> + </property> + <property name="text"> + <string>VSync</string> + </property> + </widget> + </item> + <item row="7" column="0"> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis"> + <string><html><head/><body><p>The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...</p></body></html></string> + </property> + <property name="text"> + <string>VSync interval:</string> + </property> + </widget> + </item> <item row="2" column="0" colspan="2"> <widget class="QRadioButton" name="rb3DOpenGL"> <property name="whatsThis"> <string><html><head/><body><p>The OpenGL renderer may be faster than software and supports graphical enhancements, but is more prone to glitches.</p></body></html></string> </property> <property name="text"> - <string>OpenGL</string> + <string>OpenGL (Classic)</string> </property> </widget> </item> @@ -186,6 +193,13 @@ </property> </widget> </item> + <item row="3" column="0"> + <widget class="QRadioButton" name="rb3DCompute"> + <property name="text"> + <string>OpenGL (Compute shader)</string> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index f670477..ccd1775 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -230,19 +230,17 @@ void EmuThread::initOpenGL() oglContext = windowctx; oglContext->MakeCurrent(); - OpenGL::BuildShaderProgram(kScreenVS, kScreenFS, screenShaderProgram, "ScreenShader"); - GLuint pid = screenShaderProgram[2]; - glBindAttribLocation(pid, 0, "vPosition"); - glBindAttribLocation(pid, 1, "vTexcoord"); - glBindFragDataLocation(pid, 0, "oColor"); + OpenGL::CompileVertexFragmentProgram(screenShaderProgram, + kScreenVS, kScreenFS, + "ScreenShader", + {{"vPosition", 0}, {"vTexcoord", 1}}, + {{"oColor", 0}}); - OpenGL::LinkShaderProgram(screenShaderProgram); + glUseProgram(screenShaderProgram); + glUniform1i(glGetUniformLocation(screenShaderProgram, "ScreenTex"), 0); - glUseProgram(pid); - glUniform1i(glGetUniformLocation(pid, "ScreenTex"), 0); - - screenShaderScreenSizeULoc = glGetUniformLocation(pid, "uScreenSize"); - screenShaderTransformULoc = glGetUniformLocation(pid, "uTransform"); + screenShaderScreenSizeULoc = glGetUniformLocation(screenShaderProgram, "uScreenSize"); + screenShaderTransformULoc = glGetUniformLocation(screenShaderProgram, "uTransform"); // to prevent bleeding between both parts of the screen // with bilinear filtering enabled @@ -302,7 +300,7 @@ void EmuThread::deinitOpenGL() glDeleteVertexArrays(1, &screenVertexArray); glDeleteBuffers(1, &screenVertexBuffer); - OpenGL::DeleteShaderProgram(screenShaderProgram); + glDeleteProgram(screenShaderProgram); OSD::DeInit(); @@ -328,6 +326,7 @@ void EmuThread::run() videoSettings.Soft_Threaded = Config::Threaded3D != 0; videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; + videoSettings.GL_HiresCoordinates = Config::GL_HiresCoordinates; if (mainWindow->hasOGL) { @@ -458,16 +457,17 @@ void EmuThread::run() else #endif { - videoRenderer = 0; + videoRenderer = GPU::renderer3D_Software; } - videoRenderer = oglContext ? Config::_3DRenderer : 0; + videoRenderer = oglContext ? Config::_3DRenderer : GPU::renderer3D_Software; videoSettingsDirty = false; videoSettings.Soft_Threaded = Config::Threaded3D != 0; videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; + videoSettings.GL_HiresCoordinates = Config::GL_HiresCoordinates; GPU::SetRenderSettings(videoRenderer, videoSettings); } @@ -770,7 +770,7 @@ void EmuThread::drawScreenGL() glViewport(0, 0, w, h); - glUseProgram(screenShaderProgram[2]); + glUseProgram(screenShaderProgram); glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); int frontbuf = FrontBuffer; @@ -1887,7 +1887,7 @@ void MainWindow::closeEvent(QCloseEvent* event) void MainWindow::createScreenPanel() { - hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != GPU::renderer3D_Software); if (hasOGL) { @@ -3307,13 +3307,7 @@ int main(int argc, char** argv) #define SANITIZE(var, min, max) { var = std::clamp(var, min, max); } SANITIZE(Config::ConsoleType, 0, 1); - SANITIZE(Config::_3DRenderer, - 0, - 0 // Minimum, Software renderer - #ifdef OGLRENDERER_ENABLED - + 1 // OpenGL Renderer - #endif - ); + SANITIZE(Config::_3DRenderer, (int)GPU::renderer3D_Software, (int)GPU::renderer3D_Max); SANITIZE(Config::ScreenVSyncInterval, 1, 20); SANITIZE(Config::GL_ScaleFactor, 1, 16); SANITIZE(Config::AudioInterp, 0, 3); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 5832ed3..dd927f1 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -121,7 +121,7 @@ private: GL::Context* oglContext = nullptr; GLuint screenVertexBuffer, screenVertexArray; GLuint screenTexture; - GLuint screenShaderProgram[3]; + GLuint screenShaderProgram; GLuint screenShaderTransformULoc, screenShaderScreenSizeULoc; QMutex screenSettingsLock; -- 2.42.0
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor