diff --git a/CMakeLists.txt b/CMakeLists.txt index a8915f868a..df8dbd2078 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,7 +84,7 @@ if(NOT ANDROID) endif() endif() -if(NOT WIN32 AND VULKAN) +if(VULKAN AND NOT WIN32 AND NOT ANDROID) IF (NOT Vulkan_FOUND) find_library(Vulkan_LIBRARY NAMES vulkan HINTS "$ENV{VULKAN_SDK}/lib" "${CMAKE_SOURCE_DIR}/ext/vulkan" REQUIRED) IF (Vulkan_LIBRARY) @@ -1676,7 +1676,7 @@ set(GlslangLibs glslang OGLCompiler OSDependent SPIRV SPVRemapper spirv-cross-gl target_link_libraries(${CoreLibName} Common native kirk cityhash sfmt19937 xbrz xxhash ${GlslangLibs} ${CoreExtraLibs} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} ${CMAKE_DL_LIBS}) -if(VULKAN) +if(VULKAN AND NOT ANDROID AND NOT WIN32) target_link_libraries(${CoreLibName} ${Vulkan_LIBRARY}) endif() diff --git a/Core/AVIDump.cpp b/Core/AVIDump.cpp index 6188a2bda8..37c6cd620a 100644 --- a/Core/AVIDump.cpp +++ b/Core/AVIDump.cpp @@ -174,7 +174,7 @@ void AVIDump::AddFrame() u32 h = buf.GetHeight(); CheckResolution(w, h); u8 *flipbuffer = nullptr; - const u8 *buffer = ConvertBufferTo888RGB(buf, flipbuffer, w, h); + const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, w, h); #ifdef USE_FFMPEG diff --git a/Core/Screenshot.cpp b/Core/Screenshot.cpp index bc5b046056..46694c35a1 100644 --- a/Core/Screenshot.cpp +++ b/Core/Screenshot.cpp @@ -125,12 +125,14 @@ static bool WriteScreenshotToPNG(png_imagep image, const char *filename, int con } #endif -static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, const void *buffer, int offset, bool rev) { +static bool ConvertPixelTo8888RGBA(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, u8 &a, const void *buffer, int offset, bool rev) { const u8 *buf8 = (const u8 *)buffer; const u16 *buf16 = (const u16 *)buffer; const u32 *buf32 = (const u32 *)buffer; const float *fbuf = (const float *)buffer; + // NOTE: a and r might be the same channel. This is used for RGB. + u32 src; double fsrc; switch (fmt) { @@ -139,6 +141,7 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, if (rev) { src = bswap16(src); } + a = 255; r = Convert5To8((src >> 0) & 0x1F); g = Convert6To8((src >> 5) & 0x3F); b = Convert5To8((src >> 11) & 0x1F); @@ -148,6 +151,7 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, if (rev) { src = bswap16(src); } + a = (src >> 15) ? 255 : 0; r = Convert5To8((src >> 0) & 0x1F); g = Convert5To8((src >> 5) & 0x1F); b = Convert5To8((src >> 10) & 0x1F); @@ -157,6 +161,7 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, if (rev) { src = bswap16(src); } + a = Convert4To8((src >> 12) & 0xF); r = Convert4To8((src >> 0) & 0xF); g = Convert4To8((src >> 4) & 0xF); b = Convert4To8((src >> 8) & 0xF); @@ -166,54 +171,62 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, if (rev) { src = bswap32(src); } + a = (src >> 24) & 0xFF; r = (src >> 0) & 0xFF; g = (src >> 8) & 0xFF; b = (src >> 16) & 0xFF; break; case GPU_DBG_FORMAT_FLOAT: fsrc = fbuf[offset]; - r = fsrc >= 1.0 ? 255 : (fsrc < 0.0 ? 0 : (int)(fsrc * 255.0)); + r = 255; g = 0; b = 0; + a = fsrc >= 1.0 ? 255 : (fsrc < 0.0 ? 0 : (int)(fsrc * 255.0)); break; case GPU_DBG_FORMAT_16BIT: src = buf16[offset]; - r = src >> 8; + r = 255; g = 0; b = 0; + a = src >> 8; break; case GPU_DBG_FORMAT_8BIT: src = buf8[offset]; - r = src; + r = 255; g = 0; b = 0; + a = src; break; case GPU_DBG_FORMAT_24BIT_8X: src = buf32[offset]; - r = (src >> 16) & 0xFF; + r = 255; g = 0; b = 0; + a = (src >> 16) & 0xFF; break; case GPU_DBG_FORMAT_24X_8BIT: src = buf32[offset]; - r = (src >> 24) & 0xFF; + r = 255; g = 0; b = 0; + a = (src >> 24) & 0xFF; break; case GPU_DBG_FORMAT_24BIT_8X_DIV_256: src = buf32[offset]& 0x00FFFFFF; src = src - 0x800000 + 0x8000; - r = (src >> 8) & 0xFF; + r = 255; g = 0; b = 0; + a = (src >> 8) & 0xFF; break; case GPU_DBG_FORMAT_FLOAT_DIV_256: fsrc = fbuf[offset]; src = (int)(fsrc * 16777215.0); src = src - 0x800000 + 0x8000; - r = (src >> 8) & 0xFF; + r = 255; g = 0; b = 0; + a = (src >> 8) & 0xFF; break; default: _assert_msg_(SYSTEM, false, "Unsupported framebuffer format for screenshot: %d", fmt); @@ -223,24 +236,27 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, return true; } -const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u32 &h) { +const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *&temp, u32 &w, u32 &h) { + int pixelSize = alpha ? 4 : 3; + GPUDebugBufferFormat nativeFmt = alpha ? GPU_DBG_FORMAT_8888 : GPU_DBG_FORMAT_888_RGB; + w = std::min(w, buf.GetStride()); h = std::min(h, buf.GetHeight()); // The temp buffer will be freed by the caller if set, and can be the return value. - if (buf.GetFlipped() || buf.GetFormat() != GPU_DBG_FORMAT_888_RGB) { - temp = new u8[3 * w * h]; + if (buf.GetFlipped() || buf.GetFormat() != nativeFmt) { + temp = new u8[pixelSize * w * h]; } else { temp = nullptr; } const u8 *buffer = buf.GetData(); - if (buf.GetFlipped() && buf.GetFormat() == GPU_DBG_FORMAT_888_RGB) { + if (buf.GetFlipped() && buf.GetFormat() == nativeFmt) { // Silly OpenGL reads upside down, we flip to another buffer for simplicity. for (u32 y = 0; y < h; y++) { - memcpy(temp + y * w * 3, buffer + (buf.GetHeight() - y - 1) * buf.GetStride() * 3, w * 3); + memcpy(temp + y * w * pixelSize, buffer + (buf.GetHeight() - y - 1) * buf.GetStride() * pixelSize, w * pixelSize); } - } else if (buf.GetFormat() < GPU_DBG_FORMAT_FLOAT) { + } else if (buf.GetFormat() < GPU_DBG_FORMAT_FLOAT && buf.GetFormat() != nativeFmt) { // Let's boil it down to how we need to interpret the bits. int baseFmt = buf.GetFormat() & ~(GPU_DBG_FORMAT_REVERSE_FLAG | GPU_DBG_FORMAT_BRSWAP_FLAG); bool rev = (buf.GetFormat() & GPU_DBG_FORMAT_REVERSE_FLAG) != 0; @@ -252,21 +268,22 @@ const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u3 for (u32 x = 0; x < w; x++) { u8 *dst; if (flip) { - dst = &temp[(h - y - 1) * w * 3 + x * 3]; + dst = &temp[(h - y - 1) * w * pixelSize + x * pixelSize]; } else { - dst = &temp[y * w * 3 + x * 3]; + dst = &temp[y * w * pixelSize + x * pixelSize]; } u8 &r = brswap ? dst[2] : dst[0]; u8 &g = dst[1]; u8 &b = brswap ? dst[0] : dst[2]; + u8 &a = alpha ? dst[3] : r; - if (!ConvertPixelTo888RGB(GPUDebugBufferFormat(baseFmt), r, g, b, buffer, y * buf.GetStride() + x, rev)) { + if (!ConvertPixelTo8888RGBA(GPUDebugBufferFormat(baseFmt), r, g, b, a, buffer, y * buf.GetStride() + x, rev)) { return nullptr; } } } - } else if (buf.GetFormat() != GPU_DBG_FORMAT_888_RGB) { + } else if (buf.GetFormat() != nativeFmt) { bool flip = buf.GetFlipped(); // This is pretty inefficient. @@ -274,12 +291,13 @@ const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u3 for (u32 x = 0; x < w; x++) { u8 *dst; if (flip) { - dst = &temp[(h - y - 1) * w * 3 + x * 3]; + dst = &temp[(h - y - 1) * w * pixelSize + x * pixelSize]; } else { - dst = &temp[y * w * 3 + x * 3]; + dst = &temp[y * w * pixelSize + x * pixelSize]; } - if (!ConvertPixelTo888RGB(buf.GetFormat(), dst[0], dst[1], dst[2], buffer, y * buf.GetStride() + x, false)) { + u8 &a = alpha ? dst[3] : dst[0]; + if (!ConvertPixelTo8888RGBA(buf.GetFormat(), dst[0], dst[1], dst[2], a, buffer, y * buf.GetStride() + x, false)) { return nullptr; } } @@ -316,7 +334,7 @@ bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotTy u8 *flipbuffer = nullptr; if (success) { - const u8 *buffer = ConvertBufferTo888RGB(buf, flipbuffer, w, h); + const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, w, h); success = buffer != nullptr; if (success) { if (width) @@ -364,3 +382,25 @@ bool Save888RGBScreenshot(const char *filename, ScreenshotFormat fmt, const u8 * } #endif } + +bool Save8888RGBAScreenshot(const char *filename, const u8 *buffer, int w, int h) { +#ifdef USING_QT_UI + QImage image(buffer, w, h, QImage::Format_RGBA8888); + return image.save(filename, "PNG"); +#else + png_image png; + memset(&png, 0, sizeof(png)); + png.version = PNG_IMAGE_VERSION; + png.format = PNG_FORMAT_RGBA; + png.width = w; + png.height = h; + bool success = WriteScreenshotToPNG(&png, filename, 0, buffer, w * 4, nullptr); + png_image_free(&png); + + if (png.warning_or_error >= 2) { + ERROR_LOG(SYSTEM, "Saving screenshot to PNG produced errors."); + success = false; + } + return success; +#endif +} diff --git a/Core/Screenshot.h b/Core/Screenshot.h index 75214b54a2..1632a1434c 100644 --- a/Core/Screenshot.h +++ b/Core/Screenshot.h @@ -33,8 +33,9 @@ enum ScreenshotType { SCREENSHOT_RENDER, }; -const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u32 &h); +const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *&temp, u32 &w, u32 &h); // Can only be used while in game. bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotType type, int *width = nullptr, int *height = nullptr, int maxRes = -1); bool Save888RGBScreenshot(const char *filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h); +bool Save8888RGBAScreenshot(const char *filename, const u8 *bufferRGBA8888, int w, int h); diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 9fce9a6fb5..4ea75b7d16 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -489,7 +489,7 @@ u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, } bool DrawEngineCommon::ApplyShaderBlending() { - if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH) { + if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) { return true; } diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 1b314261c0..d89ce53a60 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1970,8 +1970,11 @@ bool FramebufferManagerCommon::GetDepthbuffer(u32 fb_address, int fb_stride, u32 } else { buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT, flipY); } - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, w, h, Draw::DataFormat::D32F, buffer.GetData(), w); - return true; + // No need to free on failure, that's the caller's job (it likely will reuse a buffer.) + bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, w, h, Draw::DataFormat::D32F, buffer.GetData(), w); + // That may have unbound the framebuffer, rebind to avoid crashes when debugging. + RebindFramebuffer(); + return retval; } bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) { @@ -1996,21 +1999,22 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G } bool flipY = (g_Config.iGPUBackend == GPU_BACKEND_OPENGL && !useBufferedRendering_) ? true : false; + // No need to free on failure, the caller/destructor will do that. Usually this is a reused buffer, anyway. buffer.Allocate(w, h, GPU_DBG_FORMAT_8BIT, flipY); - if (draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, w,h, Draw::DataFormat::S8, buffer.GetData(), w)) { - return true; - } else { - buffer.Free(); - return false; - } + bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, w,h, Draw::DataFormat::S8, buffer.GetData(), w); + // That may have unbound the framebuffer, rebind to avoid crashes when debugging. + RebindFramebuffer(); + return retval; } bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { int w, h; draw_->GetFramebufferDimensions(nullptr, &w, &h); buffer.Allocate(w, h, GE_FORMAT_8888, false, true); - draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); - return true; + bool retval = draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); + // That may have unbound the framebuffer, rebind to avoid crashes when debugging. + RebindFramebuffer(); + return retval; } // This function takes an already correctly-sized framebuffer and packs it into RAM. @@ -2113,7 +2117,7 @@ void FramebufferManagerCommon::RebindFramebuffer() { if (currentRenderVfb_ && currentRenderVfb_->fbo) { draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); } else { - // Should this even happen? + // Should this even happen? It could while debugging, but maybe we can just skip binding at all. draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); } gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 023b5ccb22..e5497ea19a 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -303,7 +303,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu case GE_DSTBLEND_DOUBLESRCALPHA: // We can't technically do this correctly (due to clamping) without reading the dst color. // Using a copy isn't accurate either, though, when there's overlap. - if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH) + if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; return REPLACE_BLEND_PRE_SRC_2X_ALPHA; @@ -445,14 +445,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu case GE_DSTBLEND_DOUBLESRCALPHA: if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) { // Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap. - if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH) + if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO; return REPLACE_BLEND_PRE_SRC_2X_ALPHA; } else { // This means dst alpha/color is used in the src factor. // Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?) // We will just hope that doubling alpha for the dst factor will not clamp too badly. - if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH) + if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO; return REPLACE_BLEND_2X_ALPHA; } diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index 16df9a1609..0aa9c21193 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -58,7 +58,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform if (gl_extensions.IsGLES) { // ES doesn't support dual source alpha :( - if (gstate_c.featureFlags & GPU_SUPPORTS_GLSL_ES_300) { + if (gstate_c.Supports(GPU_SUPPORTS_GLSL_ES_300)) { WRITE(p, "#version 300 es\n"); // GLSL ES 3.0 fragColor0 = "fragColor0"; texture = "texture"; @@ -91,8 +91,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform highpFog = (gl_extensions.bugs & BUG_PVR_SHADER_PRECISION_BAD) ? true : false; highpTexcoord = highpFog; - if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH) { - if ((gstate_c.featureFlags & GPU_SUPPORTS_GLSL_ES_300) != 0 && gl_extensions.EXT_shader_framebuffer_fetch) { + if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) { + if (gstate_c.Supports(GPU_SUPPORTS_GLSL_ES_300) && gl_extensions.EXT_shader_framebuffer_fetch) { WRITE(p, "#extension GL_EXT_shader_framebuffer_fetch : require\n"); lastFragData = "fragColor0"; } else if (gl_extensions.EXT_shader_framebuffer_fetch) { @@ -535,7 +535,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform if (replaceBlend == REPLACE_BLEND_COPY_FBO) { // If we have NV_shader_framebuffer_fetch / EXT_shader_framebuffer_fetch, we skip the blit. // We can just read the prev value more directly. - if (gstate_c.featureFlags & GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH) { + if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) { WRITE(p, " lowp vec4 destColor = %s;\n", lastFragData); } else if (!texelFetch) { WRITE(p, " lowp vec4 destColor = %s(fbotex, gl_FragCoord.xy * u_fbotexSize.xy);\n", texture); diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 6fc70b7ef7..a1854df7a5 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -175,7 +175,8 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) if (g_Config.bHardwareTessellation) { // Disable hardware tessellation if device is unsupported. - if (!gstate_c.SupportsAll(GPU_SUPPORTS_INSTANCE_RENDERING | GPU_SUPPORTS_VERTEX_TEXTURE_FETCH | GPU_SUPPORTS_TEXTURE_FLOAT)) { + bool hasTexelFetch = gl_extensions.GLES3 || (!gl_extensions.IsGLES && gl_extensions.VersionGEThan(3, 3, 0)) || gl_extensions.EXT_gpu_shader4; + if (!gstate_c.SupportsAll(GPU_SUPPORTS_INSTANCE_RENDERING | GPU_SUPPORTS_VERTEX_TEXTURE_FETCH | GPU_SUPPORTS_TEXTURE_FLOAT) || !hasTexelFetch) { // TODO: Check unsupported device name list.(Above gpu features are supported but it has issues with weak gpu, memory, shader compiler etc...) g_Config.bHardwareTessellation = false; ERROR_LOG(G3D, "Hardware Tessellation is unsupported, falling back to software tessellation"); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 82668ea9f1..cde9d935a7 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -114,7 +114,7 @@ LinkedShader::LinkedShader(VShaderID VSID, Shader *vs, FShaderID FSID, Shader *f glBindAttribLocation(program, ATTR_COLOR1, "color1"); #if !defined(USING_GLES2) - if (gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND) { + if (gstate_c.Supports(GPU_SUPPORTS_DUALSOURCE_BLEND)) { // Dual source alpha glBindFragDataLocationIndexed(program, 0, 0, "fragColor0"); glBindFragDataLocationIndexed(program, 0, 1, "fragColor1"); @@ -123,7 +123,7 @@ LinkedShader::LinkedShader(VShaderID VSID, Shader *vs, FShaderID FSID, Shader *f } #elif !defined(IOS) if (gl_extensions.GLES3) { - if (gstate_c.featureFlags & GPU_SUPPORTS_DUALSOURCE_BLEND) { + if (gstate_c.Supports(GPU_SUPPORTS_DUALSOURCE_BLEND)) { glBindFragDataLocationIndexedEXT(program, 0, 0, "fragColor0"); glBindFragDataLocationIndexedEXT(program, 0, 1, "fragColor1"); } diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index f3202d24bf..04e011e314 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -36,15 +36,17 @@ SDLJoystick *joystick = NULL; #include "Common/Vulkan/VulkanDebug.h" #include "math.h" +#if !defined(__APPLE__) +#include "SDL_syswm.h" +#endif + #if defined(VK_USE_PLATFORM_XLIB_KHR) #include #include -#include "SDL_syswm.h" #elif defined(VK_USE_PLATFORM_XCB_KHR) #include #include #include -#include "SDL_syswm.h" #endif #if defined(USING_EGL) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 327a9d4358..b9d16abd70 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -99,8 +99,9 @@ bool CheckSupportInstancedTessellationGLES() { bool instanceRendering = gl_extensions.GLES3 || (canUseInstanceID && canDefInstanceID); bool textureFloat = gl_extensions.ARB_texture_float || gl_extensions.OES_texture_float; + bool hasTexelFetch = gl_extensions.GLES3 || (!gl_extensions.IsGLES && gl_extensions.VersionGEThan(3, 3, 0)) || gl_extensions.EXT_gpu_shader4; - return instanceRendering && vertexTexture && textureFloat; + return instanceRendering && vertexTexture && textureFloat && hasTexelFetch; #endif } diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 325b346a38..2b1639971f 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -157,16 +157,27 @@ void CGEDebugger::SetupPreviews() { PrimaryPreviewHover(x, y); }); primaryWindow->SetRightClickMenu(subMenu, [&] (int cmd) { + HMENU subMenu = GetSubMenu(g_hPopupMenus, POPUP_SUBMENU_ID_GEDBG_PREVIEW); switch (cmd) { + case 0: + // Setup. + CheckMenuItem(subMenu, ID_GEDBG_ENABLE_PREVIEW, MF_BYCOMMAND | ((previewsEnabled_ & 1) ? MF_CHECKED : MF_UNCHECKED)); + break; case ID_GEDBG_EXPORT_IMAGE: PreviewExport(primaryBuffer_); break; + case ID_GEDBG_ENABLE_PREVIEW: + previewsEnabled_ ^= 1; + primaryWindow->Redraw(); default: break; } return true; }); + primaryWindow->SetRedrawCallback([&] { + HandleRedraw(1); + }); primaryWindow->Clear(); } if (secondWindow == nullptr) { @@ -178,16 +189,27 @@ void CGEDebugger::SetupPreviews() { SecondPreviewHover(x, y); }); secondWindow->SetRightClickMenu(subMenu, [&] (int cmd) { + HMENU subMenu = GetSubMenu(g_hPopupMenus, POPUP_SUBMENU_ID_GEDBG_PREVIEW); switch (cmd) { + case 0: + // Setup. + CheckMenuItem(subMenu, ID_GEDBG_ENABLE_PREVIEW, MF_BYCOMMAND | ((previewsEnabled_ & 2) ? MF_CHECKED : MF_UNCHECKED)); + break; case ID_GEDBG_EXPORT_IMAGE: PreviewExport(secondBuffer_); break; + case ID_GEDBG_ENABLE_PREVIEW: + previewsEnabled_ ^= 2; + secondWindow->Redraw(); default: break; } return true; }); + secondWindow->SetRedrawCallback([&] { + HandleRedraw(2); + }); secondWindow->Clear(); } } @@ -229,13 +251,18 @@ void CGEDebugger::PreviewExport(const GPUDebugBuffer *dbgBuffer) { std::string fn; if (W32Util::BrowseForFileName(false, GetDlgHandle(), L"Save Preview Image...", nullptr, filter, L"png", fn)) { ScreenshotFormat fmt = fn.find(".jpg") != fn.npos ? ScreenshotFormat::JPG : ScreenshotFormat::PNG; + bool saveAlpha = fmt == ScreenshotFormat::PNG; u8 *flipbuffer = nullptr; u32 w = (u32)-1; u32 h = (u32)-1; - const u8 *buffer = ConvertBufferTo888RGB(*dbgBuffer, flipbuffer, w, h); + const u8 *buffer = ConvertBufferToScreenshot(*dbgBuffer, saveAlpha, flipbuffer, w, h); if (buffer != nullptr) { - Save888RGBScreenshot(fn.c_str(), fmt, buffer, w, h); + if (saveAlpha) { + Save8888RGBAScreenshot(fn.c_str(), buffer, w, h); + } else { + Save888RGBScreenshot(fn.c_str(), fmt, buffer, w, h); + } } delete [] flipbuffer; } @@ -253,19 +280,18 @@ void CGEDebugger::UpdatePreviews() { state = gpuDebug->GetGState(); } + updating_ = true; UpdateTextureLevel(textureLevel_); UpdatePrimaryPreview(state); UpdateSecondPreview(state); + u32 primOp = PrimPreviewOp(); + if (primOp != 0) { + UpdatePrimPreview(primOp, 3); + } + DisplayList list; if (gpuDebug != nullptr && gpuDebug->GetCurrentDisplayList(list)) { - const u32 op = Memory::Read_U32(list.pc); - const u32 cmd = op >> 24; - // TODO: Bezier/spline? - if (cmd == GE_CMD_PRIM && !showClut_) { - UpdatePrimPreview(op); - } - displayList->setDisplayList(list); } @@ -277,6 +303,7 @@ void CGEDebugger::UpdatePreviews() { matrices->Update(); lists->Update(); watch->Update(); + updating_ = false; } u32 CGEDebugger::TexturePreviewFlags(const GPUgstate &state) { diff --git a/Windows/GEDebugger/GEDebugger.h b/Windows/GEDebugger/GEDebugger.h index d520931054..f47afc4236 100644 --- a/Windows/GEDebugger/GEDebugger.h +++ b/Windows/GEDebugger/GEDebugger.h @@ -70,8 +70,10 @@ private: void UpdatePreviews(); void UpdatePrimaryPreview(const GPUgstate &state); void UpdateSecondPreview(const GPUgstate &state); - void UpdatePrimPreview(u32 op); + u32 PrimPreviewOp(); + void UpdatePrimPreview(u32 op, int which); void CleanupPrimPreview(); + void HandleRedraw(int which); void UpdateSize(WORD width, WORD height); void SavePosition(); void SetBreakNext(BreakNextType type); @@ -106,6 +108,8 @@ private: const GPUDebugBuffer *primaryBuffer_ = nullptr; const GPUDebugBuffer *secondBuffer_ = nullptr; + bool updating_ = false; + int previewsEnabled_ = 3; int minWidth_; int minHeight_; }; diff --git a/Windows/GEDebugger/SimpleGLWindow.cpp b/Windows/GEDebugger/SimpleGLWindow.cpp index cd848cb03a..f9c06b5c02 100644 --- a/Windows/GEDebugger/SimpleGLWindow.cpp +++ b/Windows/GEDebugger/SimpleGLWindow.cpp @@ -68,8 +68,7 @@ static const char basic_vs[] = "}\n"; SimpleGLWindow::SimpleGLWindow(HWND wnd) - : hWnd_(wnd), valid_(false), drawProgram_(nullptr), vao_(0), tex_(0), flags_(0), zoom_(false), - dragging_(false), offsetX_(0), offsetY_(0), reformatBuf_(nullptr), hoverCallback_(nullptr) { + : hWnd_(wnd) { SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR) this); } @@ -365,10 +364,23 @@ void SimpleGLWindow::GetContentSize(float &x, float &y, float &fw, float &fh) { void SimpleGLWindow::Redraw(bool andSwap) { DrawChecker(); - if (tw_ == 0 && th_ == 0) { + auto swapWithCallback = [andSwap, this]() { if (andSwap) { - Swap(); + swapped_ = false; + if (redrawCallback_ && !inRedrawCallback_) { + inRedrawCallback_ = true; + redrawCallback_(); + inRedrawCallback_ = false; + } + // In case the callback swaps, don't do it twice. + if (!swapped_) { + Swap(); + } } + }; + + if (tw_ == 0 && th_ == 0) { + swapWithCallback(); return; } @@ -412,7 +424,7 @@ void SimpleGLWindow::Redraw(bool andSwap) { glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, vao_ ? 0 : indices); if (andSwap) { - Swap(); + swapWithCallback(); } } @@ -423,7 +435,9 @@ void SimpleGLWindow::Clear() { } void SimpleGLWindow::Begin() { - Redraw(false); + if (!inRedrawCallback_) { + Redraw(false); + } if (vao_) { glBindVertexArray(0); @@ -549,6 +563,7 @@ bool SimpleGLWindow::RightClick(int mouseX, int mouseY) { POINT pt{mouseX, mouseY}; ClientToScreen(hWnd_, &pt); + rightClickCallback_(0); int result = TrackPopupMenuEx(rightClickMenu_, TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, hWnd_, 0); if (result != 0) { rightClickCallback_(result); diff --git a/Windows/GEDebugger/SimpleGLWindow.h b/Windows/GEDebugger/SimpleGLWindow.h index 5b08fcb95c..8628ab5eef 100644 --- a/Windows/GEDebugger/SimpleGLWindow.h +++ b/Windows/GEDebugger/SimpleGLWindow.h @@ -81,6 +81,7 @@ struct SimpleGLWindow { } void Swap() { + swapped_ = true; SwapBuffers(hDC_); } @@ -106,10 +107,15 @@ struct SimpleGLWindow { void GetContentSize(float &x, float &y, float &fw, float &fh); + void SetRedrawCallback(std::function callback) { + redrawCallback_ = callback; + } + void SetHoverCallback(std::function hoverCallback) { hoverCallback_ = hoverCallback; } + // Called first with 0 that it's opening, then the selected item. void SetRightClickMenu(HMENU menu, std::function callback) { rightClickCallback_ = callback; rightClickMenu_ = menu; @@ -134,7 +140,7 @@ protected: HWND hWnd_; HDC hDC_; HGLRC hGLRC_; - bool valid_; + bool valid_ = false; // Width and height of the window. int w_; int h_; @@ -143,25 +149,28 @@ protected: int th_; bool tflipped_; - GLSLProgram *drawProgram_; - GLuint vao_; - GLuint ibuf_; - GLuint vbuf_; - GLuint checker_; - GLuint tex_; - u32 flags_; + GLSLProgram *drawProgram_ = nullptr; + GLuint vao_ = 0; + GLuint ibuf_ = 0; + GLuint vbuf_ = 0; + GLuint checker_ = 0; + GLuint tex_ = 0; + u32 flags_ = 0; // Disable shrink (toggled by double click.) - bool zoom_; - bool dragging_; + bool zoom_ = false; + bool dragging_ = false; + bool inRedrawCallback_ = false; + bool swapped_ = false; int dragStartX_; int dragStartY_; u32 dragLastUpdate_; // Offset to position the texture is drawn at. - int offsetX_; - int offsetY_; - u32 *reformatBuf_; - u32 reformatBufSize_; + int offsetX_ = 0; + int offsetY_ = 0; + u32 *reformatBuf_ = nullptr; + u32 reformatBufSize_ = 0; + std::function redrawCallback_; std::function hoverCallback_; std::function rightClickCallback_; HMENU rightClickMenu_; diff --git a/Windows/GEDebugger/VertexPreview.cpp b/Windows/GEDebugger/VertexPreview.cpp index 75577a1a3c..1f78554d91 100644 --- a/Windows/GEDebugger/VertexPreview.cpp +++ b/Windows/GEDebugger/VertexPreview.cpp @@ -151,7 +151,21 @@ static void ExpandRectangles(std::vector &vertices, std::vector< count *= 3; } -void CGEDebugger::UpdatePrimPreview(u32 op) { +u32 CGEDebugger::PrimPreviewOp() { + DisplayList list; + if (gpuDebug != nullptr && gpuDebug->GetCurrentDisplayList(list)) { + const u32 op = Memory::Read_U32(list.pc); + const u32 cmd = op >> 24; + // TODO: Bezier/spline? + if (cmd == GE_CMD_PRIM && !showClut_) { + return op; + } + } + + return 0; +} + +void CGEDebugger::UpdatePrimPreview(u32 op, int which) { const u32 prim_type = (op >> 16) & 0x7; int count = op & 0xFFFF; if (prim_type >= 7) { @@ -162,7 +176,8 @@ void CGEDebugger::UpdatePrimPreview(u32 op) { ERROR_LOG(G3D, "Invalid debugging environment, shutting down?"); return; } - if (count == 0) { + which &= previewsEnabled_; + if (count == 0 || which == 0) { return; } @@ -182,17 +197,6 @@ void CGEDebugger::UpdatePrimPreview(u32 op) { float fw, fh; float x, y; - primaryWindow->Begin(); - primaryWindow->GetContentSize(x, y, fw, fh); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBlendEquation(GL_FUNC_ADD); - glBindTexture(GL_TEXTURE_2D, 0); - glViewport((GLint)x, (GLint)y, (GLsizei)fw, (GLsizei)fh); - glScissor((GLint)x, (GLint)y, (GLsizei)fw, (GLsizei)fh); - BindPreviewProgram(previewProgram); - // TODO: Probably there's a better way and place to do this. u16 minIndex = 0; u16 maxIndex = count - 1; @@ -209,124 +213,155 @@ void CGEDebugger::UpdatePrimPreview(u32 op) { } } - const float invTexWidth = 1.0f / gstate_c.curTextureWidth; - const float invTexHeight = 1.0f / gstate_c.curTextureHeight; + auto wrapCoord = [](float &coord) { + if (coord < 0.0f) { + coord += ceilf(-coord); + } + if (coord > 1.0f) { + coord -= floorf(coord); + } + }; + + const float invTexWidth = 1.0f / gpuDebug->GetGState().getTextureWidth(0); + const float invTexHeight = 1.0f / gpuDebug->GetGState().getTextureHeight(0); + const float invRealTexWidth = 1.0f / gstate_c.curTextureWidth; + const float invRealTexHeight = 1.0f / gstate_c.curTextureHeight; + bool clampS = gpuDebug->GetGState().isTexCoordClampedS(); + bool clampT = gpuDebug->GetGState().isTexCoordClampedT(); for (u16 i = minIndex; i <= maxIndex; ++i) { vertices[i].u *= invTexWidth; vertices[i].v *= invTexHeight; - if (vertices[i].u > 1.0f || vertices[i].u < 0.0f) - vertices[i].u -= floor(vertices[i].u); - if (vertices[i].v > 1.0f || vertices[i].v < 0.0f) - vertices[i].v -= floor(vertices[i].v); + if (!clampS) + wrapCoord(vertices[i].u); + if (!clampT) + wrapCoord(vertices[i].v); } - if (previewVao == 0 && gl_extensions.ARB_vertex_array_object) { - glGenVertexArrays(1, &previewVao); - glBindVertexArray(previewVao); - glEnableVertexAttribArray(previewProgram->a_position); + if (which & 1) { + primaryWindow->Begin(); + primaryWindow->GetContentSize(x, y, fw, fh); - glGenBuffers(1, &ibuf); - glGenBuffers(1, &vbuf); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); - glBindBuffer(GL_ARRAY_BUFFER, vbuf); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + glBindTexture(GL_TEXTURE_2D, 0); + // The surface is upside down, so vertical offsets are negated. + glViewport((GLint)x, (GLint)-(y + fh - primaryWindow->Height()), (GLsizei)fw, (GLsizei)fh); + glScissor((GLint)x, (GLint)-(y + fh - primaryWindow->Height()), (GLsizei)fw, (GLsizei)fh); + BindPreviewProgram(previewProgram); - glVertexAttribPointer(previewProgram->a_position, 3, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (void *)(2 * sizeof(float))); + if (previewVao == 0 && gl_extensions.ARB_vertex_array_object) { + glGenVertexArrays(1, &previewVao); + glBindVertexArray(previewVao); + glEnableVertexAttribArray(previewProgram->a_position); + + glGenBuffers(1, &ibuf); + glGenBuffers(1, &vbuf); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); + glBindBuffer(GL_ARRAY_BUFFER, vbuf); + + glVertexAttribPointer(previewProgram->a_position, 3, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (void *)(2 * sizeof(float))); + } + + if (vbuf != 0) { + glBindBuffer(GL_ARRAY_BUFFER, vbuf); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GPUDebugVertex), vertices.data(), GL_STREAM_DRAW); + } + + if (ibuf != 0 && !indices.empty()) { + glBindVertexArray(previewVao); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(u16), indices.data(), GL_STREAM_DRAW); + } + + float scale[] = { + 480.0f / (float)PSP_CoreParameter().renderWidth, + 272.0f / (float)PSP_CoreParameter().renderHeight, + }; + + Matrix4x4 ortho; + ortho.setOrtho(-(float)gstate_c.curRTOffsetX, (primaryWindow->TexWidth() - (int)gstate_c.curRTOffsetX) * scale[0], primaryWindow->TexHeight() * scale[1], 0, -1, 1); + glUniformMatrix4fv(previewProgram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr()); + if (previewVao != 0) { + glBindVertexArray(previewVao); + } else { + glEnableVertexAttribArray(previewProgram->a_position); + glVertexAttribPointer(previewProgram->a_position, 3, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (float *)vertices.data() + 2); + } + + if (indices.empty()) { + glDrawArrays(glprim[prim], 0, count); + } else { + glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, previewVao != 0 ? 0 : indices.data()); + } + + if (previewVao == 0) { + glDisableVertexAttribArray(previewProgram->a_position); + } + + primaryWindow->End(); } - if (vbuf != 0) { - glBindBuffer(GL_ARRAY_BUFFER, vbuf); - glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GPUDebugVertex), vertices.data(), GL_STREAM_DRAW); + if (which & 2) { + secondWindow->Begin(); + secondWindow->GetContentSize(x, y, fw, fh); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + glBindTexture(GL_TEXTURE_2D, 0); + // The surface is upside down, so vertical offsets are flipped. + glViewport((GLint)x, (GLint)-(y + fh - secondWindow->Height()), (GLsizei)fw, (GLsizei)fh); + glScissor((GLint)x, (GLint)-(y + fh - secondWindow->Height()), (GLsizei)fw, (GLsizei)fh); + BindPreviewProgram(texPreviewProgram); + + if (texPreviewVao == 0 && gl_extensions.ARB_vertex_array_object) { + glGenVertexArrays(1, &texPreviewVao); + glBindVertexArray(texPreviewVao); + glEnableVertexAttribArray(texPreviewProgram->a_position); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); + glBindBuffer(GL_ARRAY_BUFFER, vbuf); + + glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), 0); + } + + // TODO: For some reason we have to re-upload the data? + if (vbuf != 0) { + glBindBuffer(GL_ARRAY_BUFFER, vbuf); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GPUDebugVertex), vertices.data(), GL_STREAM_DRAW); + } + + if (ibuf != 0 && !indices.empty()) { + glBindVertexArray(texPreviewVao); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(u16), indices.data(), GL_STREAM_DRAW); + } + + Matrix4x4 ortho; + ortho.setOrtho(0.0f - (float)gstate_c.curTextureXOffset * invRealTexWidth, 1.0f - (float)gstate_c.curTextureXOffset * invRealTexWidth, 1.0f - (float)gstate_c.curTextureYOffset * invRealTexHeight, 0.0f - (float)gstate_c.curTextureYOffset * invRealTexHeight, -1.0f, 1.0f); + glUniformMatrix4fv(texPreviewProgram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr()); + if (texPreviewVao != 0) { + glBindVertexArray(texPreviewVao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); + glBindBuffer(GL_ARRAY_BUFFER, vbuf); + glEnableVertexAttribArray(texPreviewProgram->a_position); + glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), 0); + } else { + glEnableVertexAttribArray(texPreviewProgram->a_position); + glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (float *)vertices.data()); + } + + if (indices.empty()) { + glDrawArrays(glprim[prim], 0, count); + } else { + glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, texPreviewVao != 0 ? 0 : indices.data()); + } + + if (texPreviewVao == 0) { + glDisableVertexAttribArray(previewProgram->a_position); + } + + secondWindow->End(); } - - if (ibuf != 0 && !indices.empty()) { - glBindVertexArray(previewVao); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(u16), indices.data(), GL_STREAM_DRAW); - } - - float scale[] = { - 480.0f / (float)PSP_CoreParameter().renderWidth, - 272.0f / (float)PSP_CoreParameter().renderHeight, - }; - - Matrix4x4 ortho; - ortho.setOrtho(-(float)gstate_c.curRTOffsetX, (primaryWindow->TexWidth() - (int)gstate_c.curRTOffsetX) * scale[0], primaryWindow->TexHeight() * scale[1], 0, -1, 1); - glUniformMatrix4fv(previewProgram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr()); - if (previewVao != 0) { - glBindVertexArray(previewVao); - } else { - glEnableVertexAttribArray(previewProgram->a_position); - glVertexAttribPointer(previewProgram->a_position, 3, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (float *)vertices.data() + 2); - } - - if (indices.empty()) { - glDrawArrays(glprim[prim], 0, count); - } else { - glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, previewVao != 0 ? 0 : indices.data()); - } - - if (previewVao == 0) { - glDisableVertexAttribArray(previewProgram->a_position); - } - - primaryWindow->End(); - - secondWindow->Begin(); - secondWindow->GetContentSize(x, y, fw, fh); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBlendEquation(GL_FUNC_ADD); - glBindTexture(GL_TEXTURE_2D, 0); - glViewport((GLint)x, (GLint)y, (GLsizei)fw, (GLsizei)fh); - glScissor((GLint)x, (GLint)y, (GLsizei)fw, (GLsizei)fh); - BindPreviewProgram(texPreviewProgram); - - if (texPreviewVao == 0 && gl_extensions.ARB_vertex_array_object) { - glGenVertexArrays(1, &texPreviewVao); - glBindVertexArray(texPreviewVao); - glEnableVertexAttribArray(texPreviewProgram->a_position); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); - glBindBuffer(GL_ARRAY_BUFFER, vbuf); - - glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), 0); - } - - // TODO: For some reason we have to re-upload the data? - if (vbuf != 0) { - glBindBuffer(GL_ARRAY_BUFFER, vbuf); - glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GPUDebugVertex), vertices.data(), GL_STREAM_DRAW); - } - - if (ibuf != 0 && !indices.empty()) { - glBindVertexArray(texPreviewVao); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(u16), indices.data(), GL_STREAM_DRAW); - } - - ortho.setOrtho(0.0f - (float)gstate_c.curTextureXOffset * invTexWidth, 1.0f - (float)gstate_c.curTextureXOffset * invTexWidth, 1.0f - (float)gstate_c.curTextureYOffset * invTexHeight, 0.0f - (float)gstate_c.curTextureYOffset * invTexHeight, -1.0f, 1.0f); - glUniformMatrix4fv(texPreviewProgram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr()); - if (texPreviewVao != 0) { - glBindVertexArray(texPreviewVao); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); - glBindBuffer(GL_ARRAY_BUFFER, vbuf); - glEnableVertexAttribArray(texPreviewProgram->a_position); - glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), 0); - } else { - glEnableVertexAttribArray(texPreviewProgram->a_position); - glVertexAttribPointer(texPreviewProgram->a_position, 2, GL_FLOAT, GL_FALSE, sizeof(GPUDebugVertex), (float *)vertices.data()); - } - - if (indices.empty()) { - glDrawArrays(glprim[prim], 0, count); - } else { - glDrawElements(glprim[prim], count, GL_UNSIGNED_SHORT, texPreviewVao != 0 ? 0 : indices.data()); - } - - if (texPreviewVao == 0) { - glDisableVertexAttribArray(previewProgram->a_position); - } - - secondWindow->End(); } void CGEDebugger::CleanupPrimPreview() { @@ -337,3 +372,14 @@ void CGEDebugger::CleanupPrimPreview() { glsl_destroy(texPreviewProgram); } } + +void CGEDebugger::HandleRedraw(int which) { + if (updating_) { + return; + } + + u32 op = PrimPreviewOp(); + if (op) { + UpdatePrimPreview(op, which); + } +} diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index d6c62fa829..d099b406ed 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -657,6 +657,7 @@ BEGIN POPUP "gepreviewoptions" BEGIN MENUITEM "Export Image...", ID_GEDBG_EXPORT_IMAGE + MENUITEM "Show Prim Preview" ID_GEDBG_ENABLE_PREVIEW END END diff --git a/Windows/resource.h b/Windows/resource.h index ac043ae57b..c8cbb00066 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -335,6 +335,7 @@ #define ID_HELP_GITHUB 40168 #define IDC_GEDBG_RECORD 40169 #define ID_GEDBG_EXPORT_IMAGE 40170 +#define ID_GEDBG_ENABLE_PREVIEW 40171 #define ID_SIMPLEUI_TOGGLE 40200 #define ID_SIMPLEUI_HIDE 40201 @@ -349,7 +350,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 256 -#define _APS_NEXT_COMMAND_VALUE 40171 +#define _APS_NEXT_COMMAND_VALUE 40172 #define _APS_NEXT_CONTROL_VALUE 1200 #define _APS_NEXT_SYMED_VALUE 101 #endif