From f7604297929139473c99003ed81439c3d4a8abe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 9 Oct 2017 15:17:55 +0200 Subject: [PATCH 001/111] Minor refactoring of framebuffer readbacks. --- GPU/GLES/FramebufferManagerGLES.cpp | 41 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index e51aad78a6..1255eb1b59 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -920,9 +920,15 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { // Order packing/readback of the framebuffer if (vfb) { - int pixelType, pixelSize, pixelFormat, align; + GLuint pixelType, pixelFormat; bool reverseOrder = gstate_c.Supports(GPU_PREFER_REVERSE_COLOR_ORDER); + int pixelSize = 2, align = 2; + if (vfb->format == GE_FORMAT_8888) { + pixelSize = 4; + align = 4; + } + switch (vfb->format) { // GL_UNSIGNED_INT_8_8_8_8 returns A B G R (little-endian, tested in Nvidia card/x86 PC) // GL_UNSIGNED_BYTE returns R G B A in consecutive bytes ("big-endian"/not treated as 32-bit value) @@ -934,8 +940,6 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_SHORT_4_4_4_4); #endif pixelFormat = GL_RGBA; - pixelSize = 2; - align = 2; break; case GE_FORMAT_5551: // 16 bit RGBA #ifdef USING_GLES2 @@ -944,8 +948,6 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_SHORT_5_5_5_1); #endif pixelFormat = GL_RGBA; - pixelSize = 2; - align = 2; break; case GE_FORMAT_565: // 16 bit RGB #ifdef USING_GLES2 @@ -954,20 +956,25 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_5_6_5_REV : GL_UNSIGNED_SHORT_5_6_5); #endif pixelFormat = GL_RGB; - pixelSize = 2; - align = 2; break; case GE_FORMAT_8888: // 32 bit RGBA default: pixelType = GL_UNSIGNED_BYTE; pixelFormat = GL_RGBA; - pixelSize = 4; - align = 4; break; } + if (useCPU) { + // If converting pixel formats on the CPU we'll always request RGBA8888 + // Otherwise we'll directly request the format we need and let the GPU sort it out + pixelSize = 4; + align = 4; + pixelFormat = GL_RGBA; + pixelType = GL_UNSIGNED_BYTE; + } + // If using the CPU, we need 4 bytes per pixel always. - u32 bufSize = vfb->fb_stride * vfb->height * (useCPU ? 4 : pixelSize); + u32 bufSize = vfb->fb_stride * vfb->height * pixelSize; u32 fb_address = (0x04000000) | vfb->fb_address; if (vfb->fbo) { @@ -985,15 +992,8 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { pixelBufObj_[currentPBO_].maxSize = bufSize; } - if (useCPU) { - // If converting pixel formats on the CPU we'll always request RGBA8888 - glPixelStorei(GL_PACK_ALIGNMENT, 4); - SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, GL_RGBA, GL_UNSIGNED_BYTE, 0); - } else { - // Otherwise we'll directly request the format we need and let the GPU sort it out - glPixelStorei(GL_PACK_ALIGNMENT, align); - SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, pixelFormat, pixelType, 0); - } + glPixelStorei(GL_PACK_ALIGNMENT, align); + SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, pixelFormat, pixelType, 0); unbind = true; @@ -1054,10 +1054,9 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); glPixelStorei(GL_PACK_ALIGNMENT, 4); - GLenum glfmt = GL_RGBA; CHECK_GL_ERROR_IF_DEBUG(); - SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->fb_stride, h, glfmt, GL_UNSIGNED_BYTE, packed); + SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->fb_stride, h, GL_RGBA, GL_UNSIGNED_BYTE, packed); if (convert) { ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); From 0aa950b663c7135fd77071a54f157a997190ba6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 9 Oct 2017 15:43:26 +0200 Subject: [PATCH 002/111] Start moving PACK_ALIGNMENT into SafeGLReadPixels --- GPU/GLES/FramebufferManagerGLES.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 1255eb1b59..aa52f5bbee 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -845,6 +845,21 @@ static void LogReadPixelsError(GLenum error) { static void SafeGLReadPixels(GLint x, GLint y, GLsizei w, GLsizei h, GLenum fmt, GLenum type, void *pixels) { CHECK_GL_ERROR_IF_DEBUG(); + + // Apply the correct alignment. + if (fmt == GL_RGBA && type == GL_UNSIGNED_BYTE) { + glPixelStorei(GL_PACK_ALIGNMENT, 4); + } else if (fmt == GL_RGB && type == GL_UNSIGNED_BYTE) { + glPixelStorei(GL_PACK_ALIGNMENT, 1); + } else if (fmt == GL_DEPTH_COMPONENT && type == GL_FLOAT) { + glPixelStorei(GL_PACK_ALIGNMENT, 4); + } else if (fmt == GL_STENCIL_INDEX && type == GL_UNSIGNED_BYTE) { + glPixelStorei(GL_PACK_ALIGNMENT, 2); // This seems dubious. 1? + } else { + // The remainders do their own calls for now. + // glPixelStorei(GL_PACK_ALIGNMENT, 4); + } + if (!gl_extensions.IsGLES || (gl_extensions.GLES3 && gl_extensions.gpuVendor != GPU_VENDOR_NVIDIA)) { // Some drivers seem to require we specify this. See #8254. glPixelStorei(GL_PACK_ROW_LENGTH, w); @@ -1053,9 +1068,6 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x if (packed) { DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - CHECK_GL_ERROR_IF_DEBUG(); - SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->fb_stride, h, GL_RGBA, GL_UNSIGNED_BYTE, packed); if (convert) { @@ -1098,7 +1110,6 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int DEBUG_LOG(FRAMEBUF, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address); - glPixelStorei(GL_PACK_ALIGNMENT, 4); SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->z_stride, h, GL_DEPTH_COMPONENT, GL_FLOAT, convBuf_); int dstByteOffset = y * vfb->fb_stride * sizeof(u16); @@ -1260,7 +1271,6 @@ bool FramebufferManagerGLES::GetFramebuffer(u32 fb_address, int fb_stride, GEBuf if (gl_extensions.GLES3 || !gl_extensions.IsGLES) glReadBuffer(GL_COLOR_ATTACHMENT0); - glPixelStorei(GL_PACK_ALIGNMENT, 4); SafeGLReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); // We may have blitted to a temp FBO. @@ -1275,7 +1285,6 @@ bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { // The backbuffer is flipped. buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true); - glPixelStorei(GL_PACK_ALIGNMENT, 1); SafeGLReadPixels(0, 0, pw, ph, GL_RGB, GL_UNSIGNED_BYTE, buffer.GetData()); CHECK_GL_ERROR_IF_DEBUG(); return true; @@ -1306,7 +1315,6 @@ bool FramebufferManagerGLES::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z draw_->BindFramebufferForRead(vfb->fbo); if (gl_extensions.GLES3 || !gl_extensions.IsGLES) glReadBuffer(GL_COLOR_ATTACHMENT0); - glPixelStorei(GL_PACK_ALIGNMENT, 4); SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_DEPTH_COMPONENT, GL_FLOAT, buffer.GetData()); CHECK_GL_ERROR_IF_DEBUG(); return true; @@ -1331,7 +1339,6 @@ bool FramebufferManagerGLES::GetStencilbuffer(u32 fb_address, int fb_stride, GPU draw_->BindFramebufferForRead(vfb->fbo); if (gl_extensions.GLES3 || !gl_extensions.IsGLES) glReadBuffer(GL_COLOR_ATTACHMENT0); - glPixelStorei(GL_PACK_ALIGNMENT, 2); SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, buffer.GetData()); CHECK_GL_ERROR_IF_DEBUG(); return true; From ec7dc724c75fd7a303f6c8425bc1fe120edbc158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 9 Oct 2017 15:51:43 +0200 Subject: [PATCH 003/111] Minor OpenGL refactor. --- ext/native/thin3d/thin3d_gl.cpp | 54 ++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 12c6b0d6b2..060dbe2c4b 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -729,11 +729,33 @@ void OpenGLTexture::AutoGenMipmaps() { } } -void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { - int internalFormat; - int format; - int type; +static bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type) { + switch (fmt) { + case DataFormat::R8G8B8A8_UNORM: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + return true; + case DataFormat::B4G4R4A4_UNORM_PACK16: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_4_4_4_4; + return true; +#ifndef USING_GLES2 + case DataFormat::A4R4G4B4_UNORM_PACK16: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_4_4_4_4_REV; + return true; +#endif + default: + ELOG("Thin3d GL: Unsupported texture format %d", (int)fmt); + return false; + } +} + +void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { if (width != width_ || height != height_ || depth != depth_) { // When switching to texStorage we need to handle this correctly. width_ = width; @@ -741,26 +763,10 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth_ = depth; } - switch (format_) { - case DataFormat::R8G8B8A8_UNORM: - internalFormat = GL_RGBA; - format = GL_RGBA; - type = GL_UNSIGNED_BYTE; - break; - case DataFormat::B4G4R4A4_UNORM_PACK16: - internalFormat = GL_RGBA; - format = GL_RGBA; - type = GL_UNSIGNED_SHORT_4_4_4_4; - break; -#ifndef USING_GLES2 - case DataFormat::A4R4G4B4_UNORM_PACK16: - internalFormat = GL_RGBA; - format = GL_RGBA; - type = GL_UNSIGNED_SHORT_4_4_4_4_REV; - break; -#endif - default: - ELOG("Thin3d GL: Unsupported texture format %d", (int)format_); + GLuint internalFormat; + GLuint format; + GLuint type; + if (!Thin3DFormatToFormatAndType(format_, internalFormat, format, type)) { return; } From 2d94d453898bee77681f524a37fa6170d34cc2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 9 Oct 2017 15:58:03 +0200 Subject: [PATCH 004/111] More format support in thin3d GL --- GPU/GLES/FramebufferManagerGLES.cpp | 16 +++++++++++++ ext/native/thin3d/thin3d_gl.cpp | 35 ++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index aa52f5bbee..0297996d87 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -944,6 +944,22 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { align = 4; } + Draw::DataFormat dataFmt = Draw::DataFormat::UNDEFINED; + switch (vfb->format) { + case GE_FORMAT_4444: + dataFmt = (reverseOrder ? Draw::DataFormat::A4R4G4B4_UNORM_PACK16 : Draw::DataFormat::B4G4R4A4_UNORM_PACK16); + break; + case GE_FORMAT_5551: + dataFmt = (reverseOrder ? Draw::DataFormat::A1R5G5B5_UNORM_PACK16 : Draw::DataFormat::B5G5R5A1_UNORM_PACK16); + break; + case GE_FORMAT_565: + dataFmt = (reverseOrder ? Draw::DataFormat::R5G6B5_UNORM_PACK16 : Draw::DataFormat::B5G6R5_UNORM_PACK16); + break; + case GE_FORMAT_8888: + dataFmt = Draw::DataFormat::R8G8B8A8_UNORM; + break; + }; + switch (vfb->format) { // GL_UNSIGNED_INT_8_8_8_8 returns A B G R (little-endian, tested in Nvidia card/x86 PC) // GL_UNSIGNED_BYTE returns R G B A in consecutive bytes ("big-endian"/not treated as 32-bit value) diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 060dbe2c4b..2d60235794 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -729,6 +729,7 @@ void OpenGLTexture::AutoGenMipmaps() { } } +// TODO: Also output storage format (GL_RGB8 etc) for modern GL usage. static bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type) { switch (fmt) { case DataFormat::R8G8B8A8_UNORM: @@ -736,25 +737,57 @@ static bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, format = GL_RGBA; type = GL_UNSIGNED_BYTE; return true; + + case DataFormat::R8G8B8_UNORM: + internalFormat = GL_RGB; + format = GL_RGB; + type = GL_UNSIGNED_BYTE; + return true; + case DataFormat::B4G4R4A4_UNORM_PACK16: internalFormat = GL_RGBA; format = GL_RGBA; type = GL_UNSIGNED_SHORT_4_4_4_4; return true; + + case DataFormat::B5G6R5_UNORM_PACK16: + internalFormat = GL_RGB; + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + return true; + + case DataFormat::B5G5R5A1_UNORM_PACK16: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_5_5_5_1; + return true; + #ifndef USING_GLES2 case DataFormat::A4R4G4B4_UNORM_PACK16: internalFormat = GL_RGBA; format = GL_RGBA; type = GL_UNSIGNED_SHORT_4_4_4_4_REV; return true; + + case DataFormat::R5G6B5_UNORM_PACK16: + internalFormat = GL_RGB; + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5_REV; + return true; + + case DataFormat::A1R5G5B5_UNORM_PACK16: + internalFormat = GL_RGBA; + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + return true; #endif + default: ELOG("Thin3d GL: Unsupported texture format %d", (int)fmt); return false; } } - void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { if (width != width_ || height != height_ || depth != depth_) { // When switching to texStorage we need to handle this correctly. From 5bbe0eb69a0ebd268110529e54a4f86925df283d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Oct 2017 14:26:35 +0200 Subject: [PATCH 005/111] Move GL readback to a Thin3D abstraction --- GPU/D3D11/FramebufferManagerD3D11.cpp | 18 +++-- GPU/D3D11/FramebufferManagerD3D11.h | 2 +- GPU/GLES/FramebufferManagerGLES.cpp | 112 ++++---------------------- ext/native/thin3d/thin3d.h | 6 ++ ext/native/thin3d/thin3d_gl.cpp | 81 ++++++++++++++++--- 5 files changed, 102 insertions(+), 117 deletions(-) diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index f5d55de6f1..2768aa8609 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -599,12 +599,12 @@ void FramebufferManagerD3D11::ReadFramebufferToMemory(VirtualFramebuffer *vfb, b OptimizeDownloadRange(vfb, x, y, w, h); if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) { // No need to blit - PackFramebufferD3D11_(vfb, x, y, w, h); + PackFramebufferSync_(vfb, x, y, w, h); } else { VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - PackFramebufferD3D11_(nvfb, x, y, w, h); + PackFramebufferSync_(nvfb, x, y, w, h); } textureCacheD3D11_->ForgetLastTexture(); @@ -639,7 +639,7 @@ void FramebufferManagerD3D11::DownloadFramebufferForClut(u32 fb_address, u32 loa VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - PackFramebufferD3D11_(nvfb, x, y, w, h); + PackFramebufferSync_(nvfb, x, y, w, h); textureCacheD3D11_->ForgetLastTexture(); RebindFramebuffer(); @@ -815,7 +815,7 @@ void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 wid // This function takes an already correctly-sized framebuffer and packs it into RAM. // Does not need to account for scaling. // Color conversion is currently done on CPU but should be done on GPU. -void FramebufferManagerD3D11::PackFramebufferD3D11_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { +void FramebufferManagerD3D11::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferD3D11_: vfb->fbo == 0"); return; @@ -824,12 +824,16 @@ void FramebufferManagerD3D11::PackFramebufferD3D11_(VirtualFramebuffer *vfb, int const u32 fb_address = (0x04000000) | vfb->fb_address; const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; + // TODO: Handle the other formats? We don't currently create them, I think. + const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; + u8 *destPtr = Memory::GetPointer(fb_address + dstByteOffset); + // We always need to convert from the framebuffer native format. // Right now that's always 8888. DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address); ID3D11Texture2D *colorTex = (ID3D11Texture2D *)draw_->GetFramebufferAPITexture(vfb->fbo, Draw::FB_COLOR_BIT, 0); - // TODO: Only copy the necessary rectangle. + // Only copy the necessary rectangle. D3D11_BOX srcBox{ (UINT)x, (UINT)y, 0, (UINT)(x+w), (UINT)(y+h), 1 }; context_->CopySubresourceRegion(packTexture_, 0, x, y, 0, colorTex, 0, &srcBox); @@ -843,11 +847,9 @@ void FramebufferManagerD3D11::PackFramebufferD3D11_(VirtualFramebuffer *vfb, int return; } - // TODO: Handle the other formats? We don't currently create them, I think. const int srcByteOffset = y * map.RowPitch + x * 4; - const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; // Pixel size always 4 here because we always request BGRA8888. - ConvertFromRGBA8888(Memory::GetPointer(fb_address + dstByteOffset), (u8 *)map.pData + srcByteOffset, vfb->fb_stride, map.RowPitch/4, w, h, vfb->format); + ConvertFromRGBA8888(destPtr, (u8 *)map.pData + srcByteOffset, vfb->fb_stride, map.RowPitch/4, w, h, vfb->format); context_->Unmap(packTexture_, 0); } diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 3e71678119..7d9b34f1f7 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -94,7 +94,7 @@ private: void BindPostShader(const PostShaderUniforms &uniforms) override; void Bind2DShader() override; void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; - void PackFramebufferD3D11_(VirtualFramebuffer *vfb, int x, int y, int w, int h); + void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); void SimpleBlit( Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 0297996d87..15873e2234 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -843,39 +843,6 @@ static void LogReadPixelsError(GLenum error) { } #endif -static void SafeGLReadPixels(GLint x, GLint y, GLsizei w, GLsizei h, GLenum fmt, GLenum type, void *pixels) { - CHECK_GL_ERROR_IF_DEBUG(); - - // Apply the correct alignment. - if (fmt == GL_RGBA && type == GL_UNSIGNED_BYTE) { - glPixelStorei(GL_PACK_ALIGNMENT, 4); - } else if (fmt == GL_RGB && type == GL_UNSIGNED_BYTE) { - glPixelStorei(GL_PACK_ALIGNMENT, 1); - } else if (fmt == GL_DEPTH_COMPONENT && type == GL_FLOAT) { - glPixelStorei(GL_PACK_ALIGNMENT, 4); - } else if (fmt == GL_STENCIL_INDEX && type == GL_UNSIGNED_BYTE) { - glPixelStorei(GL_PACK_ALIGNMENT, 2); // This seems dubious. 1? - } else { - // The remainders do their own calls for now. - // glPixelStorei(GL_PACK_ALIGNMENT, 4); - } - - if (!gl_extensions.IsGLES || (gl_extensions.GLES3 && gl_extensions.gpuVendor != GPU_VENDOR_NVIDIA)) { - // Some drivers seem to require we specify this. See #8254. - glPixelStorei(GL_PACK_ROW_LENGTH, w); - } - - glReadPixels(x, y, w, h, fmt, type, pixels); -#ifdef DEBUG_READ_PIXELS - LogReadPixelsError(glGetError()); -#endif - - if (!gl_extensions.IsGLES || gl_extensions.GLES3) { - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - } - CHECK_GL_ERROR_IF_DEBUG(); -} - void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { CHECK_GL_ERROR_IF_DEBUG(); const int MAX_PBO = 2; @@ -935,15 +902,7 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { // Order packing/readback of the framebuffer if (vfb) { - GLuint pixelType, pixelFormat; - bool reverseOrder = gstate_c.Supports(GPU_PREFER_REVERSE_COLOR_ORDER); - int pixelSize = 2, align = 2; - if (vfb->format == GE_FORMAT_8888) { - pixelSize = 4; - align = 4; - } - Draw::DataFormat dataFmt = Draw::DataFormat::UNDEFINED; switch (vfb->format) { case GE_FORMAT_4444: @@ -960,49 +919,12 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { break; }; - switch (vfb->format) { - // GL_UNSIGNED_INT_8_8_8_8 returns A B G R (little-endian, tested in Nvidia card/x86 PC) - // GL_UNSIGNED_BYTE returns R G B A in consecutive bytes ("big-endian"/not treated as 32-bit value) - // We want R G B A, so we use *_REV for 16-bit formats and GL_UNSIGNED_BYTE for 32-bit - case GE_FORMAT_4444: // 16 bit RGBA -#ifdef USING_GLES2 - pixelType = GL_UNSIGNED_SHORT_4_4_4_4; -#else - pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_SHORT_4_4_4_4); -#endif - pixelFormat = GL_RGBA; - break; - case GE_FORMAT_5551: // 16 bit RGBA -#ifdef USING_GLES2 - pixelType = GL_UNSIGNED_SHORT_5_5_5_1; -#else - pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_SHORT_5_5_5_1); -#endif - pixelFormat = GL_RGBA; - break; - case GE_FORMAT_565: // 16 bit RGB -#ifdef USING_GLES2 - pixelType = GL_UNSIGNED_SHORT_5_6_5; -#else - pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_5_6_5_REV : GL_UNSIGNED_SHORT_5_6_5); -#endif - pixelFormat = GL_RGB; - break; - case GE_FORMAT_8888: // 32 bit RGBA - default: - pixelType = GL_UNSIGNED_BYTE; - pixelFormat = GL_RGBA; - break; + if (useCPU) { + dataFmt = Draw::DataFormat::R8G8B8A8_UNORM; } - if (useCPU) { - // If converting pixel formats on the CPU we'll always request RGBA8888 - // Otherwise we'll directly request the format we need and let the GPU sort it out - pixelSize = 4; - align = 4; - pixelFormat = GL_RGBA; - pixelType = GL_UNSIGNED_BYTE; - } + int pixelSize = (int)DataFormatSizeInBytes(dataFmt); + int align = pixelSize; // If using the CPU, we need 4 bytes per pixel always. u32 bufSize = vfb->fb_stride * vfb->height * pixelSize; @@ -1023,8 +945,8 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { pixelBufObj_[currentPBO_].maxSize = bufSize; } - glPixelStorei(GL_PACK_ALIGNMENT, align); - SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, pixelFormat, pixelType, 0); + // TODO: Change to CopyFramebufferToBuffer with a proper pointer. + draw_->CopyFramebufferToMemorySync(0, 0, vfb->fb_stride, vfb->height, dataFmt, nullptr); unbind = true; @@ -1083,14 +1005,14 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x if (packed) { DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); - - SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->fb_stride, h, GL_RGBA, GL_UNSIGNED_BYTE, packed); - + int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? + draw_->CopyFramebufferToMemorySync(0, y, packW, h, Draw::DataFormat::R8G8B8A8_UNORM, packed); if (convert) { ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); } } + // TODO: Move this into Thin3d. if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) { #ifdef USING_GLES2 // GLES3 doesn't support using GL_READ_FRAMEBUFFER here. @@ -1126,7 +1048,7 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int DEBUG_LOG(FRAMEBUF, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address); - SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->z_stride, h, GL_DEPTH_COMPONENT, GL_FLOAT, convBuf_); + draw_->CopyFramebufferToMemorySync(0, y, h == 1 ? packWidth : vfb->z_stride, h, Draw::DataFormat::D32F, convBuf_); int dstByteOffset = y * vfb->fb_stride * sizeof(u16); u16 *depth = (u16 *)Memory::GetPointer(z_address + dstByteOffset); @@ -1287,7 +1209,7 @@ bool FramebufferManagerGLES::GetFramebuffer(u32 fb_address, int fb_stride, GEBuf if (gl_extensions.GLES3 || !gl_extensions.IsGLES) glReadBuffer(GL_COLOR_ATTACHMENT0); - SafeGLReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData()); // We may have blitted to a temp FBO. RebindFramebuffer(); @@ -1299,9 +1221,9 @@ bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { int pw = PSP_CoreParameter().pixelWidth; int ph = PSP_CoreParameter().pixelHeight; - // The backbuffer is flipped. + // The backbuffer is flipped (last bool) buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true); - SafeGLReadPixels(0, 0, pw, ph, GL_RGB, GL_UNSIGNED_BYTE, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(0, 0, pw, ph, Draw::DataFormat::R8G8B8_UNORM, buffer.GetData()); CHECK_GL_ERROR_IF_DEBUG(); return true; } @@ -1329,9 +1251,7 @@ bool FramebufferManagerGLES::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z } if (vfb->fbo) draw_->BindFramebufferForRead(vfb->fbo); - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) - glReadBuffer(GL_COLOR_ATTACHMENT0); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_DEPTH_COMPONENT, GL_FLOAT, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::D32F, buffer.GetData()); CHECK_GL_ERROR_IF_DEBUG(); return true; } @@ -1353,9 +1273,7 @@ bool FramebufferManagerGLES::GetStencilbuffer(u32 fb_address, int fb_stride, GPU buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, !useBufferedRendering_); if (vfb->fbo) draw_->BindFramebufferForRead(vfb->fbo); - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) - glReadBuffer(GL_COLOR_ATTACHMENT0); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::S8, buffer.GetData()); CHECK_GL_ERROR_IF_DEBUG(); return true; #else diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index a8749940d4..76ac3abb1f 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -617,6 +617,12 @@ public: virtual void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) = 0; virtual bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) = 0; + virtual bool CopyFramebufferToBuffer(Framebuffer *src, Buffer *buffer, Draw::DataFormat bufferFormat) { + return false; + } + virtual bool CopyFramebufferToMemorySync(int x, int y, int w, int h, Draw::DataFormat format, void *pixels) { + return false; + } // These functions should be self explanatory. // Binding a zero render target means binding the backbuffer. diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 2d60235794..dac1a91237 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "base/logging.h" #include "math/dataconv.h" @@ -465,6 +466,7 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; + bool CopyFramebufferToMemorySync(int x, int y, int w, int h, Draw::DataFormat format, void *pixels) override; // These functions should be self explanatory. void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override; @@ -729,63 +731,85 @@ void OpenGLTexture::AutoGenMipmaps() { } } -// TODO: Also output storage format (GL_RGB8 etc) for modern GL usage. -static bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type) { +// TODO: Also output storage format (GL_RGBA8 etc) for modern GL usage. +static bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment) { + alignment = 4; switch (fmt) { case DataFormat::R8G8B8A8_UNORM: internalFormat = GL_RGBA; format = GL_RGBA; type = GL_UNSIGNED_BYTE; - return true; + break; + + case DataFormat::D32F: + internalFormat = GL_DEPTH_COMPONENT; + format = GL_DEPTH_COMPONENT; + type = GL_FLOAT; + break; + + case DataFormat::S8: + internalFormat = GL_STENCIL_INDEX; + format = GL_STENCIL_INDEX; + type = GL_UNSIGNED_BYTE; + alignment = 1; + break; case DataFormat::R8G8B8_UNORM: internalFormat = GL_RGB; format = GL_RGB; type = GL_UNSIGNED_BYTE; - return true; + alignment = 1; + break; case DataFormat::B4G4R4A4_UNORM_PACK16: internalFormat = GL_RGBA; format = GL_RGBA; type = GL_UNSIGNED_SHORT_4_4_4_4; - return true; + alignment = 2; + break; case DataFormat::B5G6R5_UNORM_PACK16: internalFormat = GL_RGB; format = GL_RGB; type = GL_UNSIGNED_SHORT_5_6_5; - return true; + alignment = 2; + break; case DataFormat::B5G5R5A1_UNORM_PACK16: internalFormat = GL_RGBA; format = GL_RGBA; type = GL_UNSIGNED_SHORT_5_5_5_1; - return true; + alignment = 2; + break; #ifndef USING_GLES2 case DataFormat::A4R4G4B4_UNORM_PACK16: internalFormat = GL_RGBA; format = GL_RGBA; type = GL_UNSIGNED_SHORT_4_4_4_4_REV; - return true; + alignment = 2; + break; case DataFormat::R5G6B5_UNORM_PACK16: internalFormat = GL_RGB; format = GL_RGB; type = GL_UNSIGNED_SHORT_5_6_5_REV; - return true; + alignment = 2; + break; case DataFormat::A1R5G5B5_UNORM_PACK16: internalFormat = GL_RGBA; format = GL_RGBA; type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - return true; + alignment = 2; + break; #endif default: ELOG("Thin3d GL: Unsupported texture format %d", (int)fmt); return false; } + return true; } void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { @@ -799,7 +823,8 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int GLuint internalFormat; GLuint format; GLuint type; - if (!Thin3DFormatToFormatAndType(format_, internalFormat, format, type)) { + int alignment; + if (!Thin3DFormatToFormatAndType(format_, internalFormat, format, type, alignment)) { return; } @@ -815,6 +840,40 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int CHECK_GL_ERROR_IF_DEBUG(); } +bool OpenGLContext::CopyFramebufferToMemorySync(int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels) { + // Reads from the "bound for read" framebuffer. + if (gl_extensions.GLES3 || !gl_extensions.IsGLES) + glReadBuffer(GL_COLOR_ATTACHMENT0); + + CHECK_GL_ERROR_IF_DEBUG(); + + GLuint internalFormat; + GLuint format; + GLuint type; + int alignment; + if (!Thin3DFormatToFormatAndType(dataFormat, internalFormat, format, type, alignment)) { + assert(false); + } + // Apply the correct alignment. + glPixelStorei(GL_PACK_ALIGNMENT, alignment); + if (!gl_extensions.IsGLES || (gl_extensions.GLES3 && gl_extensions.gpuVendor != GPU_VENDOR_NVIDIA)) { + // Some drivers seem to require we specify this. See #8254. + glPixelStorei(GL_PACK_ROW_LENGTH, w); + } + + glReadPixels(x, y, w, h, format, type, pixels); +#ifdef DEBUG_READ_PIXELS + LogReadPixelsError(glGetError()); +#endif + + if (!gl_extensions.IsGLES || gl_extensions.GLES3) { + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + } + CHECK_GL_ERROR_IF_DEBUG(); + return true; +} + + Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) { return new OpenGLTexture(desc); } From 16e68aa7c73cfc4325087eb8e0fc15a1dd780151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Oct 2017 14:46:47 +0200 Subject: [PATCH 006/111] Get rid of BindFramebufferForRead --- GPU/GLES/FramebufferManagerGLES.cpp | 40 ++++++--------- ext/native/thin3d/thin3d.h | 3 +- ext/native/thin3d/thin3d_d3d11.cpp | 5 -- ext/native/thin3d/thin3d_d3d9.cpp | 1 - ext/native/thin3d/thin3d_gl.cpp | 76 ++++++++++++++--------------- ext/native/thin3d/thin3d_vulkan.cpp | 3 -- 6 files changed, 52 insertions(+), 76 deletions(-) diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 15873e2234..ed3dd6abec 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -930,9 +930,7 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { u32 bufSize = vfb->fb_stride * vfb->height * pixelSize; u32 fb_address = (0x04000000) | vfb->fb_address; - if (vfb->fbo) { - draw_->BindFramebufferForRead(vfb->fbo); - } else { + if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferAsync_: vfb->fbo == 0"); return; } @@ -946,7 +944,7 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { } // TODO: Change to CopyFramebufferToBuffer with a proper pointer. - draw_->CopyFramebufferToMemorySync(0, 0, vfb->fb_stride, vfb->height, dataFmt, nullptr); + draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, 0, vfb->fb_stride, vfb->height, dataFmt, nullptr); unbind = true; @@ -967,9 +965,7 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { } void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (vfb->fbo) { - draw_->BindFramebufferForRead(vfb->fbo); - } else { + if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0"); return; } @@ -1006,7 +1002,7 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x if (packed) { DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? - draw_->CopyFramebufferToMemorySync(0, y, packW, h, Draw::DataFormat::R8G8B8A8_UNORM, packed); + draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, y, packW, h, Draw::DataFormat::R8G8B8A8_UNORM, packed); if (convert) { ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); } @@ -1028,9 +1024,7 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x } void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (vfb->fbo) { - draw_->BindFramebufferForRead(vfb->fbo); - } else { + if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0"); return; } @@ -1048,7 +1042,7 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int DEBUG_LOG(FRAMEBUF, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address); - draw_->CopyFramebufferToMemorySync(0, y, h == 1 ? packWidth : vfb->z_stride, h, Draw::DataFormat::D32F, convBuf_); + draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, y, h == 1 ? packWidth : vfb->z_stride, h, Draw::DataFormat::D32F, convBuf_); int dstByteOffset = y * vfb->fb_stride * sizeof(u16); u16 *depth = (u16 *)Memory::GetPointer(z_address + dstByteOffset); @@ -1185,6 +1179,9 @@ bool FramebufferManagerGLES::GetFramebuffer(u32 fb_address, int fb_stride, GEBuf } int w = vfb->renderWidth, h = vfb->renderHeight; + + Draw::Framebuffer *bound = nullptr; + if (vfb->fbo) { if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) { w = vfb->width * maxRes; @@ -1199,17 +1196,14 @@ bool FramebufferManagerGLES::GetFramebuffer(u32 fb_address, int fb_stride, GEBuf tempVfb.renderHeight = h; BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0); - draw_->BindFramebufferForRead(tempFBO); + bound = tempFBO; } else { - draw_->BindFramebufferForRead(vfb->fbo); + bound = vfb->fbo; } } buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) - glReadBuffer(GL_COLOR_ATTACHMENT0); - - draw_->CopyFramebufferToMemorySync(0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(bound, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData()); // We may have blitted to a temp FBO. RebindFramebuffer(); @@ -1223,7 +1217,7 @@ bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { // The backbuffer is flipped (last bool) buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true); - draw_->CopyFramebufferToMemorySync(0, 0, pw, ph, Draw::DataFormat::R8G8B8_UNORM, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(nullptr, 0, 0, pw, ph, Draw::DataFormat::R8G8B8_UNORM, buffer.GetData()); CHECK_GL_ERROR_IF_DEBUG(); return true; } @@ -1249,9 +1243,7 @@ bool FramebufferManagerGLES::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z } else { buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, !useBufferedRendering_); } - if (vfb->fbo) - draw_->BindFramebufferForRead(vfb->fbo); - draw_->CopyFramebufferToMemorySync(0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::D32F, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::D32F, buffer.GetData()); CHECK_GL_ERROR_IF_DEBUG(); return true; } @@ -1271,9 +1263,7 @@ bool FramebufferManagerGLES::GetStencilbuffer(u32 fb_address, int fb_stride, GPU #ifndef USING_GLES2 buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, !useBufferedRendering_); - if (vfb->fbo) - draw_->BindFramebufferForRead(vfb->fbo); - draw_->CopyFramebufferToMemorySync(0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::S8, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::S8, buffer.GetData()); CHECK_GL_ERROR_IF_DEBUG(); return true; #else diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 76ac3abb1f..d3431ca2e5 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -620,7 +620,7 @@ public: virtual bool CopyFramebufferToBuffer(Framebuffer *src, Buffer *buffer, Draw::DataFormat bufferFormat) { return false; } - virtual bool CopyFramebufferToMemorySync(int x, int y, int w, int h, Draw::DataFormat format, void *pixels) { + virtual bool CopyFramebufferToMemorySync(Framebuffer *src, int x, int y, int w, int h, Draw::DataFormat format, void *pixels) { return false; } @@ -630,7 +630,6 @@ public: // color must be 0, for now. virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) = 0; - virtual void BindFramebufferForRead(Framebuffer *fbo) = 0; virtual uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) = 0; diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index e5d26f6bb1..a57ab83479 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -61,7 +61,6 @@ public: void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override; // color must be 0, for now. void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override; - void BindFramebufferForRead(Framebuffer *fbo) override; uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override; @@ -1327,10 +1326,6 @@ void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, F context_->PSSetShaderResources(binding, 1, &fb->colorSRView); } -void D3D11DrawContext::BindFramebufferForRead(Framebuffer *fbo) { - // This is meaningless in D3D11 -} - uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo; switch (channelBit) { diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 0a4e1938e4..eef76d9ffa 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -490,7 +490,6 @@ public: void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override; // color must be 0, for now. void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override; - void BindFramebufferForRead(Framebuffer *fbo) override {} uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index dac1a91237..d1b6b804af 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -466,13 +466,12 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; - bool CopyFramebufferToMemorySync(int x, int y, int w, int h, Draw::DataFormat format, void *pixels) override; + bool CopyFramebufferToMemorySync(Framebuffer *src, int x, int y, int w, int h, Draw::DataFormat format, void *pixels) override; // These functions should be self explanatory. void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override; // color must be 0, for now. void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override; - void BindFramebufferForRead(Framebuffer *fbo) override; uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override; @@ -731,6 +730,37 @@ void OpenGLTexture::AutoGenMipmaps() { } } +class OpenGLFramebuffer : public Framebuffer, public GfxResourceHolder { +public: + OpenGLFramebuffer() { + register_gl_resource_holder(this, "framebuffer", 0); + } + ~OpenGLFramebuffer(); + + void GLLost() override { + handle = 0; + color_texture = 0; + z_stencil_buffer = 0; + z_buffer = 0; + stencil_buffer = 0; + } + + void GLRestore() override { + ELOG("Restoring framebuffers not yet implemented"); + } + + GLuint handle = 0; + GLuint color_texture = 0; + GLuint z_stencil_buffer = 0; // Either this is set, or the two below. + GLuint z_buffer = 0; + GLuint stencil_buffer = 0; + + int width; + int height; + FBColorDepth colorDepth; +}; + + // TODO: Also output storage format (GL_RGBA8 etc) for modern GL usage. static bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment) { alignment = 4; @@ -840,7 +870,10 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int CHECK_GL_ERROR_IF_DEBUG(); } -bool OpenGLContext::CopyFramebufferToMemorySync(int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels) { +bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels) { + OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src; + fbo_bind_fb_target(true, fb ? fb->handle : 0); + // Reads from the "bound for read" framebuffer. if (gl_extensions.GLES3 || !gl_extensions.IsGLES) glReadBuffer(GL_COLOR_ATTACHMENT0); @@ -1341,36 +1374,6 @@ void OpenGLInputLayout::Unapply() { } } -class OpenGLFramebuffer : public Framebuffer, public GfxResourceHolder { -public: - OpenGLFramebuffer() { - register_gl_resource_holder(this, "framebuffer", 0); - } - ~OpenGLFramebuffer(); - - void GLLost() override { - handle = 0; - color_texture = 0; - z_stencil_buffer = 0; - z_buffer = 0; - stencil_buffer = 0; - } - - void GLRestore() override { - ELOG("Restoring framebuffers not yet implemented"); - } - - GLuint handle = 0; - GLuint color_texture = 0; - GLuint z_stencil_buffer = 0; // Either this is set, or the two below. - GLuint z_buffer = 0; - GLuint stencil_buffer = 0; - - int width; - int height; - FBColorDepth colorDepth; -}; - // On PC, we always use GL_DEPTH24_STENCIL8. // On Android, we try to use what's available. @@ -1685,13 +1688,6 @@ void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Render CHECK_GL_ERROR_IF_DEBUG(); } -// For GL_EXT_FRAMEBUFFER_BLIT and similar. -void OpenGLContext::BindFramebufferForRead(Framebuffer *fbo) { - OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; - fbo_bind_fb_target(true, fb->handle); - CHECK_GL_ERROR_IF_DEBUG(); -} - void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc; OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b715d5ea23..7522060d09 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -392,7 +392,6 @@ public: void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override; // color must be 0, for now. void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override; - void BindFramebufferForRead(Framebuffer *fbo) override; uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override; @@ -2022,8 +2021,6 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne fb->color.layout = barrier.newLayout; } -void VKContext::BindFramebufferForRead(Framebuffer *fbo) { /* noop */ } - uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { // TODO: Insert transition at the end of the previous command buffer, or the one that rendered to it last. VKFramebuffer *fb = (VKFramebuffer *)fbo; From 62972cb8915dc362085778511686a8d20ca28d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Oct 2017 16:51:44 +0200 Subject: [PATCH 007/111] D3D11: Move to CopyFramebufferToSync where easily possible. --- GPU/D3D11/FramebufferManagerD3D11.cpp | 133 ++++---------------------- GPU/D3D11/FramebufferManagerD3D11.h | 4 - GPU/GLES/FramebufferManagerGLES.cpp | 21 ++-- Windows/GPU/D3D11Context.cpp | 4 - ext/native/thin3d/thin3d.cpp | 14 +++ ext/native/thin3d/thin3d.h | 9 +- ext/native/thin3d/thin3d_d3d11.cpp | 124 +++++++++++++++++++++++- ext/native/thin3d/thin3d_gl.cpp | 6 +- 8 files changed, 176 insertions(+), 139 deletions(-) diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 2768aa8609..9f72382682 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -133,22 +133,9 @@ FramebufferManagerD3D11::FramebufferManagerD3D11(Draw::DrawContext *draw) ShaderTranslationInit(); CompilePostShader(); - - D3D11_TEXTURE2D_DESC packDesc{}; - packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - packDesc.BindFlags = 0; - packDesc.Width = 512; // 512x512 is the maximum size of a framebuffer on the PSP. - packDesc.Height = 512; - packDesc.ArraySize = 1; - packDesc.MipLevels = 1; - packDesc.Usage = D3D11_USAGE_STAGING; - packDesc.SampleDesc.Count = 1; - packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTexture_)); } FramebufferManagerD3D11::~FramebufferManagerD3D11() { - packTexture_->Release(); ShaderTranslationShutdown(); // Drawing cleanup @@ -762,53 +749,18 @@ void FramebufferManagerD3D11::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, false); } -// TODO: SSE/NEON -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) -void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { - // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. - const u32 *src32 = (const u32 *)src; - - if (format == GE_FORMAT_8888) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - for (u32 y = 0; y < height; ++y) { - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } - } - } else { - // But here it shouldn't matter if they do intersect - u16 *dst16 = (u16 *)dst; - switch (format) { - case GE_FORMAT_565: // BGR 565 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGB565(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case GE_FORMAT_5551: // ABGR 1555 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA5551(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case GE_FORMAT_4444: // ABGR 4444 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA4444(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case GE_FORMAT_8888: - case GE_FORMAT_INVALID: - // Not possible. - break; - } +static Draw::DataFormat GEFormatToThin3D(int geFormat) { + switch (geFormat) { + case GE_FORMAT_4444: + return Draw::DataFormat::A4R4G4B4_UNORM_PACK16; + case GE_FORMAT_5551: + return Draw::DataFormat::A1R5G5B5_UNORM_PACK16; + case GE_FORMAT_565: + return Draw::DataFormat::R5G6B5_UNORM_PACK16; + case GE_FORMAT_8888: + return Draw::DataFormat::R8G8B8A8_UNORM; + default: + return Draw::DataFormat::UNDEFINED; } } @@ -822,7 +774,9 @@ void FramebufferManagerD3D11::PackFramebufferSync_(VirtualFramebuffer *vfb, int } const u32 fb_address = (0x04000000) | vfb->fb_address; - const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; + + Draw::DataFormat destFormat = GEFormatToThin3D(vfb->format); + const int dstBpp = (int)DataFormatSizeInBytes(destFormat); // TODO: Handle the other formats? We don't currently create them, I think. const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; @@ -831,26 +785,8 @@ void FramebufferManagerD3D11::PackFramebufferSync_(VirtualFramebuffer *vfb, int // We always need to convert from the framebuffer native format. // Right now that's always 8888. DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address); - ID3D11Texture2D *colorTex = (ID3D11Texture2D *)draw_->GetFramebufferAPITexture(vfb->fbo, Draw::FB_COLOR_BIT, 0); - // Only copy the necessary rectangle. - D3D11_BOX srcBox{ (UINT)x, (UINT)y, 0, (UINT)(x+w), (UINT)(y+h), 1 }; - context_->CopySubresourceRegion(packTexture_, 0, x, y, 0, colorTex, 0, &srcBox); - - // Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game - // does a once-off copy, that won't work at all. - - // BIG GPU STALL - D3D11_MAPPED_SUBRESOURCE map; - HRESULT result = context_->Map(packTexture_, 0, D3D11_MAP_READ, 0, &map); - if (FAILED(result)) { - return; - } - - const int srcByteOffset = y * map.RowPitch + x * 4; - // Pixel size always 4 here because we always request BGRA8888. - ConvertFromRGBA8888(destPtr, (u8 *)map.pData + srcByteOffset, vfb->fb_stride, map.RowPitch/4, w, h, vfb->format); - context_->Unmap(packTexture_, 0); + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride); } // Nobody calls this yet. @@ -953,7 +889,7 @@ bool FramebufferManagerD3D11::GetFramebuffer(u32 fb_address, int fb_stride, GEBu } int w = vfb->renderWidth, h = vfb->renderHeight; - Draw::Framebuffer *fboForRead = nullptr; + Draw::Framebuffer *bound = nullptr; if (vfb->fbo) { if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) { w = vfb->width * maxRes; @@ -968,44 +904,17 @@ bool FramebufferManagerD3D11::GetFramebuffer(u32 fb_address, int fb_stride, GEBu tempVfb.renderHeight = h; BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0); - fboForRead = tempFBO; + bound = tempFBO; } else { - fboForRead = vfb->fbo; + bound = vfb->fbo; } } - if (!fboForRead) + if (!bound) return false; buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); - ID3D11Texture2D *packTex; - D3D11_TEXTURE2D_DESC packDesc{}; - packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - packDesc.BindFlags = 0; - packDesc.Width = w; - packDesc.Height = h; - packDesc.ArraySize = 1; - packDesc.MipLevels = 1; - packDesc.Usage = D3D11_USAGE_STAGING; - packDesc.SampleDesc.Count = 1; - packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTex)); - - ID3D11Texture2D *nativeTex = (ID3D11Texture2D *)draw_->GetFramebufferAPITexture(fboForRead, Draw::FB_COLOR_BIT, 0); - context_->CopyResource(packTex, nativeTex); - - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map); - - for (int y = 0; y < h; y++) { - uint8_t *dest = (uint8_t *)buffer.GetData() + y * w * 4; - const uint8_t *src = ((const uint8_t *)map.pData) + map.RowPitch * y; - memcpy(dest, src, 4 * w); - } - - context_->Unmap(packTex, 0); - packTex->Release(); - return true; + return draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); } bool FramebufferManagerD3D11::GetDepthStencilBuffer(VirtualFramebuffer *vfb, GPUDebugBuffer &buffer, bool stencil) { diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 7d9b34f1f7..3ef5f5c7bf 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -135,10 +135,6 @@ private: ShaderManagerD3D11 *shaderManagerD3D11_; DrawEngineD3D11 *drawEngine_; - // Permanent 1:1 readback texture, 512x512 fixed - // For larger debug readbacks, we create/destroy textures on the fly. - ID3D11Texture2D *packTexture_; - // Used by post-processing shader // Postprocessing ID3D11VertexShader *postVertexShader_ = nullptr; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index ed3dd6abec..f623120a15 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -943,8 +943,8 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { pixelBufObj_[currentPBO_].maxSize = bufSize; } - // TODO: Change to CopyFramebufferToBuffer with a proper pointer. - draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, 0, vfb->fb_stride, vfb->height, dataFmt, nullptr); + // TODO: This is a hack since PBOs have not been implemented in Thin3D yet (and maybe shouldn't? maybe should do this internally?) + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->fb_stride, vfb->height, dataFmt, nullptr, vfb->fb_stride); unbind = true; @@ -1002,7 +1002,7 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x if (packed) { DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? - draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, y, packW, h, Draw::DataFormat::R8G8B8A8_UNORM, packed); + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, packW, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packW); if (convert) { ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); } @@ -1042,7 +1042,8 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int DEBUG_LOG(FRAMEBUF, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address); - draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, y, h == 1 ? packWidth : vfb->z_stride, h, Draw::DataFormat::D32F, convBuf_); + int packW = h == 1 ? packWidth : vfb->z_stride; + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, y, packW, h, Draw::DataFormat::D32F, convBuf_, packW); int dstByteOffset = y * vfb->fb_stride * sizeof(u16); u16 *depth = (u16 *)Memory::GetPointer(z_address + dstByteOffset); @@ -1203,12 +1204,10 @@ bool FramebufferManagerGLES::GetFramebuffer(u32 fb_address, int fb_stride, GEBuf } buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); - draw_->CopyFramebufferToMemorySync(bound, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData()); - + bool retval = draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); // We may have blitted to a temp FBO. RebindFramebuffer(); - CHECK_GL_ERROR_IF_DEBUG(); - return true; + return retval; } bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { @@ -1217,7 +1216,7 @@ bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { // The backbuffer is flipped (last bool) buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true); - draw_->CopyFramebufferToMemorySync(nullptr, 0, 0, pw, ph, Draw::DataFormat::R8G8B8_UNORM, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, pw, ph, Draw::DataFormat::R8G8B8_UNORM, buffer.GetData(), pw); CHECK_GL_ERROR_IF_DEBUG(); return true; } @@ -1243,7 +1242,7 @@ bool FramebufferManagerGLES::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z } else { buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, !useBufferedRendering_); } - draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::D32F, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::D32F, buffer.GetData(), vfb->renderWidth); CHECK_GL_ERROR_IF_DEBUG(); return true; } @@ -1263,7 +1262,7 @@ bool FramebufferManagerGLES::GetStencilbuffer(u32 fb_address, int fb_stride, GPU #ifndef USING_GLES2 buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, !useBufferedRendering_); - draw_->CopyFramebufferToMemorySync(vfb->fbo, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::S8, buffer.GetData()); + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::S8, buffer.GetData(), vfb->renderWidth); CHECK_GL_ERROR_IF_DEBUG(); return true; #else diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index ffd87e29ee..bed1ff2f8d 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -27,10 +27,6 @@ D3D11Context::~D3D11Context() { void D3D11Context::SwapBuffers() { swapChain_->Present(0, 0); draw_->HandleEvent(Draw::Event::PRESENTED, 0, 0, nullptr, nullptr); - - // Might be a good idea. - // context_->ClearState(); - // } void D3D11Context::SwapInterval(int interval) { diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 46ddbf3c53..44dc740664 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -41,6 +41,20 @@ size_t DataFormatSizeInBytes(DataFormat fmt) { } } +bool DataFormatIsDepthStencil(DataFormat fmt) { + switch (fmt) { + case DataFormat::D16: + case DataFormat::D24_S8: + case DataFormat::S8: + case DataFormat::D32F: + case DataFormat::D32F_S8: + return true; + default: + return false; + } +} + + bool RefCountedObject::Release() { if (refcount_ > 0 && refcount_ < 10000) { refcount_--; diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index d3431ca2e5..3c8dcc73e0 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -617,10 +617,7 @@ public: virtual void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) = 0; virtual bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) = 0; - virtual bool CopyFramebufferToBuffer(Framebuffer *src, Buffer *buffer, Draw::DataFormat bufferFormat) { - return false; - } - virtual bool CopyFramebufferToMemorySync(Framebuffer *src, int x, int y, int w, int h, Draw::DataFormat format, void *pixels) { + virtual bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) { return false; } @@ -704,6 +701,10 @@ protected: }; size_t DataFormatSizeInBytes(DataFormat fmt); +bool DataFormatIsDepthStencil(DataFormat fmt); +inline bool DataFormatIsColor(DataFormat fmt) { + return !DataFormatIsDepthStencil(fmt); +} DrawContext *T3DCreateGLContext(); diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index a57ab83479..d8b83229a8 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -10,6 +10,9 @@ #include "math/dataconv.h" #include "util/text/utf8.h" +#include "Common/ColorConv.h" + +#include #include #include #include @@ -27,7 +30,6 @@ class D3D11DepthStencilState; class D3D11SamplerState; class D3D11RasterState; - class D3D11DrawContext : public DrawContext { public: D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd); @@ -56,6 +58,7 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; + bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride); // These functions should be self explanatory. void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override; @@ -193,6 +196,9 @@ private: uint8_t stencilRef_; bool stencilRefDirty_; + // Temporaries + ID3D11Texture2D *packTexture_ = nullptr; + // System info D3D_FEATURE_LEVEL featureLevel_; std::string adapterDesc_; @@ -237,9 +243,24 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de dxgiDevice->Release(); } CreatePresets(); + + D3D11_TEXTURE2D_DESC packDesc{}; + packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + packDesc.BindFlags = 0; + packDesc.Width = 512; // 512x512 is the maximum size of a framebuffer on the PSP. + packDesc.Height = 512; + packDesc.ArraySize = 1; + packDesc.MipLevels = 1; + packDesc.Usage = D3D11_USAGE_STAGING; + packDesc.SampleDesc.Count = 1; + packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + hr = device_->CreateTexture2D(&packDesc, nullptr, &packTexture_); + assert(SUCCEEDED(hr)); } D3D11DrawContext::~D3D11DrawContext() { + packTexture_->Release(); + // Release references. ID3D11RenderTargetView *view = nullptr; context_->OMSetRenderTargets(1, &view, nullptr); @@ -1285,6 +1306,107 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, return false; } +// TODO: SSE/NEON +// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) +void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, Draw::DataFormat format) { + // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. + const u32 *src32 = (const u32 *)src; + + if (format == Draw::DataFormat::R8G8B8A8_UNORM) { + u32 *dst32 = (u32 *)dst; + if (src == dst) { + return; + } else { + for (u32 y = 0; y < height; ++y) { + memcpy(dst32, src32, width * 4); + src32 += srcStride; + dst32 += dstStride; + } + } + } else { + // But here it shouldn't matter if they do intersect + u16 *dst16 = (u16 *)dst; + switch (format) { + case Draw::DataFormat::R5G6B5_UNORM_PACK16: // BGR 565 + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGB565(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case Draw::DataFormat::A1R5G5B5_UNORM_PACK16: // ABGR 1555 + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA5551(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case Draw::DataFormat::A4R4G4B4_UNORM_PACK16: // ABGR 4444 + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA4444(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case Draw::DataFormat::R8G8B8A8_UNORM: + case Draw::DataFormat::UNDEFINED: + // Not possible. + break; + } + } +} + +bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) { + D3D11Framebuffer *fb = (D3D11Framebuffer *)src; + + assert(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM); + + bool useGlobalPacktex = (x + w <= 512 && y + h <= 512); + + ID3D11Texture2D *packTex; + if (!useGlobalPacktex) { + D3D11_TEXTURE2D_DESC packDesc{}; + packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + packDesc.BindFlags = 0; + packDesc.Width = w; + packDesc.Height = h; + packDesc.ArraySize = 1; + packDesc.MipLevels = 1; + packDesc.Usage = D3D11_USAGE_STAGING; + packDesc.SampleDesc.Count = 1; + packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + device_->CreateTexture2D(&packDesc, nullptr, &packTex); + + context_->CopyResource(packTex, fb->colorTex); + } else { + packTex = packTexture_; + } + + // Only copy the necessary rectangle. + D3D11_BOX srcBox{ (UINT)x, (UINT)y, 0, (UINT)(x + w), (UINT)(y + h), 1 }; + context_->CopySubresourceRegion(packTex, 0, x, y, 0, fb->colorTex, 0, &srcBox); + + // Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game + // does a once-off copy, that won't work at all. + + // BIG GPU STALL + D3D11_MAPPED_SUBRESOURCE map; + HRESULT result = context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map); + if (FAILED(result)) { + return false; + } + + const int srcByteOffset = y * map.RowPitch + x * 4; + // Pixel size always 4 here because we always request BGRA8888. + ConvertFromRGBA8888((u8 *)pixels, (u8 *)map.pData + srcByteOffset, pixelStride, map.RowPitch / 4, w, h, format); + context_->Unmap(packTex, 0); + + if (!useGlobalPacktex) { + packTex->Release(); + } + return true; +} + void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { // TODO: deviceContext1 can actually discard. Useful on Windows Mobile. if (fbo) { diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index d1b6b804af..c48b027085 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -466,7 +466,7 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; - bool CopyFramebufferToMemorySync(Framebuffer *src, int x, int y, int w, int h, Draw::DataFormat format, void *pixels) override; + bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) override; // These functions should be self explanatory. void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override; @@ -870,7 +870,7 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int CHECK_GL_ERROR_IF_DEBUG(); } -bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels) { +bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src; fbo_bind_fb_target(true, fb ? fb->handle : 0); @@ -891,7 +891,7 @@ bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int x, int y, glPixelStorei(GL_PACK_ALIGNMENT, alignment); if (!gl_extensions.IsGLES || (gl_extensions.GLES3 && gl_extensions.gpuVendor != GPU_VENDOR_NVIDIA)) { // Some drivers seem to require we specify this. See #8254. - glPixelStorei(GL_PACK_ROW_LENGTH, w); + glPixelStorei(GL_PACK_ROW_LENGTH, pixelStride); } glReadPixels(x, y, w, h, format, type, pixels); From 9b1ee6fce68abb9a0dc95e806dbec72f3f934ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Oct 2017 13:37:00 +0200 Subject: [PATCH 008/111] Unify GetFramebuffer in GLES and D3D11. D3D9 is left behind for now, and Vulkan still needs implementation - got a branch with initial work towards that. --- GPU/Common/FramebufferCommon.cpp | 43 +++++++++++++++++++++++++++ GPU/Common/FramebufferCommon.h | 2 +- GPU/D3D11/FramebufferManagerD3D11.cpp | 42 -------------------------- GPU/D3D11/FramebufferManagerD3D11.h | 1 - GPU/GLES/FramebufferManagerGLES.cpp | 43 --------------------------- GPU/GLES/FramebufferManagerGLES.h | 1 - 6 files changed, 44 insertions(+), 88 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index f1ec9de4a5..a49781ee2d 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1835,3 +1835,46 @@ void FramebufferManagerCommon::ShowScreenResolution() { host->NotifyUserMessage(messageStream.str(), 2.0f, 0xFFFFFF, "resize"); INFO_LOG(SYSTEM, "%s", messageStream.str().c_str()); } + +bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) { + VirtualFramebuffer *vfb = currentRenderVfb_; + if (!vfb) { + vfb = GetVFBAt(fb_address); + } + + if (!vfb) { + // If there's no vfb and we're drawing there, must be memory? + buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, format); + return true; + } + + int w = vfb->renderWidth, h = vfb->renderHeight; + + Draw::Framebuffer *bound = nullptr; + + if (vfb->fbo) { + if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) { + w = vfb->width * maxRes; + h = vfb->height * maxRes; + + Draw::Framebuffer *tempFBO = GetTempFBO(w, h); + VirtualFramebuffer tempVfb = *vfb; + tempVfb.fbo = tempFBO; + tempVfb.bufferWidth = vfb->width; + tempVfb.bufferHeight = vfb->height; + tempVfb.renderWidth = w; + tempVfb.renderHeight = h; + BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0); + + bound = tempFBO; + } else { + bound = vfb->fbo; + } + } + + buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); + bool retval = draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); + // We may have blitted to a temp FBO. + RebindFramebuffer(); + return retval; +} diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 27aef50cab..0e6ba30a65 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -278,7 +278,7 @@ public: Draw::Framebuffer *GetTempFBO(u16 w, u16 h, Draw::FBColorDepth colorDepth = Draw::FBO_8888); // Debug features - virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) = 0; + virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes); virtual bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) = 0; virtual bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) = 0; virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer) = 0; diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 9f72382682..94cabe7509 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -875,48 +875,6 @@ void FramebufferManagerD3D11::Resized() { CompilePostShader(); } -// Lots of this code could be shared (like the downsampling). -bool FramebufferManagerD3D11::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, format); - return true; - } - - int w = vfb->renderWidth, h = vfb->renderHeight; - Draw::Framebuffer *bound = nullptr; - if (vfb->fbo) { - if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) { - w = vfb->width * maxRes; - h = vfb->height * maxRes; - - Draw::Framebuffer *tempFBO = GetTempFBO(w, h); - VirtualFramebuffer tempVfb = *vfb; - tempVfb.fbo = tempFBO; - tempVfb.bufferWidth = vfb->width; - tempVfb.bufferHeight = vfb->height; - tempVfb.renderWidth = w; - tempVfb.renderHeight = h; - BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0); - - bound = tempFBO; - } else { - bound = vfb->fbo; - } - } - if (!bound) - return false; - - buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); - - return draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); -} - bool FramebufferManagerD3D11::GetDepthStencilBuffer(VirtualFramebuffer *vfb, GPUDebugBuffer &buffer, bool stencil) { int w = vfb->renderWidth, h = vfb->renderHeight; Draw::Framebuffer *fboForRead = nullptr; diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 3ef5f5c7bf..dda9419725 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -67,7 +67,6 @@ public: virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) override; bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index f623120a15..aa1665e3b7 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -1167,49 +1167,6 @@ void FramebufferManagerGLES::Resized() { } } -bool FramebufferManagerGLES::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, format); - return true; - } - - int w = vfb->renderWidth, h = vfb->renderHeight; - - Draw::Framebuffer *bound = nullptr; - - if (vfb->fbo) { - if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) { - w = vfb->width * maxRes; - h = vfb->height * maxRes; - - Draw::Framebuffer *tempFBO = GetTempFBO(w, h); - VirtualFramebuffer tempVfb = *vfb; - tempVfb.fbo = tempFBO; - tempVfb.bufferWidth = vfb->width; - tempVfb.bufferHeight = vfb->height; - tempVfb.renderWidth = w; - tempVfb.renderHeight = h; - BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0); - - bound = tempFBO; - } else { - bound = vfb->fbo; - } - } - - buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); - bool retval = draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); - // We may have blitted to a temp FBO. - RebindFramebuffer(); - return retval; -} - bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { int pw = PSP_CoreParameter().pixelWidth; int ph = PSP_CoreParameter().pixelHeight; diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 7c43c11b24..b860ea92dd 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -84,7 +84,6 @@ public: bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) override; bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; From ed602a3319b02efa283f160b2571ceb7f07fefe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Oct 2017 14:27:50 +0200 Subject: [PATCH 009/111] UWP buildfix. See comments in #9575 --- .gitignore | 1 + UWP/CommonUWP/CommonUWP.vcxproj | 20 ++++++++++---------- UWP/CoreUWP/CoreUWP.vcxproj | 22 ++++++++++++---------- UWP/CoreUWP/CoreUWP.vcxproj.filters | 6 ++++++ UWP/GPU_UWP/GPU_UWP.vcxproj | 20 ++++++++++---------- UWP/NativeUWP/NativeUWP.vcxproj | 23 ++++++++++++----------- UWP/NativeUWP/NativeUWP.vcxproj.filters | 5 ++++- UWP/SPIRVCross_UWP/SPIRVCross_UWP.vcxproj | 20 ++++++++++---------- UWP/StorageFileLoader.cpp | 12 ------------ UWP/StorageFileLoader.h | 4 +--- UWP/UI_UWP/UI_UWP.vcxproj | 20 ++++++++++---------- UWP/UWP.vcxproj | 20 ++++++++++---------- UWP/UWPHost.cpp | 4 ---- UWP/UWPHost.h | 2 -- UWP/glslang_UWP/glslang_UWP.vcxproj | 20 ++++++++++---------- UWP/libkirk_UWP/libkirk_UWP.vcxproj | 20 ++++++++++---------- UWP/zlib_UWP/zlib_UWP.vcxproj | 20 ++++++++++---------- ext/native/gfx_es2/gpu_features.cpp | 4 ++++ 18 files changed, 120 insertions(+), 123 deletions(-) diff --git a/.gitignore b/.gitignore index 5b59310999..10ecce6bab 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ PPSSPPDebug64.exe.manifest PPSSPPDebug.exe.manifest assets/flash0 UWP/icph +UWP/.vs # For Mac .DS_Store diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index f79045110f..828b6b150d 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 9bb7322606..b4e4bb6ea9 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 @@ -405,6 +405,7 @@ + @@ -596,6 +597,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 0d7268912a..119ffa5bc1 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -617,6 +617,9 @@ MIPS\IR + + HLE + @@ -1152,5 +1155,8 @@ MIPS\IR + + HLE + \ No newline at end of file diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index b78ee46e09..b7d1f40921 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/NativeUWP/NativeUWP.vcxproj b/UWP/NativeUWP/NativeUWP.vcxproj index 9c2153302f..f107dbabb7 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj +++ b/UWP/NativeUWP/NativeUWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 @@ -1180,6 +1180,7 @@ + NotUsing @@ -1315,4 +1316,4 @@ - + \ No newline at end of file diff --git a/UWP/NativeUWP/NativeUWP.vcxproj.filters b/UWP/NativeUWP/NativeUWP.vcxproj.filters index b009776eec..c7e48c5f7a 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj.filters +++ b/UWP/NativeUWP/NativeUWP.vcxproj.filters @@ -454,6 +454,9 @@ base + + math + @@ -720,4 +723,4 @@ gfx - + \ No newline at end of file diff --git a/UWP/SPIRVCross_UWP/SPIRVCross_UWP.vcxproj b/UWP/SPIRVCross_UWP/SPIRVCross_UWP.vcxproj index 8acb031408..d36a6fec45 100644 --- a/UWP/SPIRVCross_UWP/SPIRVCross_UWP.vcxproj +++ b/UWP/SPIRVCross_UWP/SPIRVCross_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/StorageFileLoader.cpp b/UWP/StorageFileLoader.cpp index 9e90b8b2ef..f758912460 100644 --- a/UWP/StorageFileLoader.cpp +++ b/UWP/StorageFileLoader.cpp @@ -137,18 +137,6 @@ void StorageFileLoader::EnsureOpen() { Sleep(100); } -// Note that multithreaded use could wreak havoc with this... -void StorageFileLoader::Seek(s64 absolutePos) { - EnsureOpen(); - seekPos_ = absolutePos; -} - -size_t StorageFileLoader::Read(size_t bytes, size_t count, void *data, Flags flags) { - size_t bytesRead = ReadAt(seekPos_, bytes, count, data, flags); - seekPos_ += bytesRead; - return bytesRead; -} - size_t StorageFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags) { EnsureOpen(); if (operationRequested_ || responseAvailable_) diff --git a/UWP/StorageFileLoader.h b/UWP/StorageFileLoader.h index 11b35ecb0e..0a85a533bd 100644 --- a/UWP/StorageFileLoader.h +++ b/UWP/StorageFileLoader.h @@ -26,9 +26,7 @@ public: s64 FileSize() override; std::string Path() const override; std::string Extension() override; - void Seek(s64 absolutePos) override; - size_t Read(size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override; size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override; private: @@ -87,4 +85,4 @@ private: class StorageDirectoryWrapper { private: std::thread thread_; -}; \ No newline at end of file +}; diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj index eada47b3b6..377dff98d5 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj +++ b/UWP/UI_UWP/UI_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/UWP.vcxproj b/UWP/UWP.vcxproj index 4be8a7258b..16f370ea04 100644 --- a/UWP/UWP.vcxproj +++ b/UWP/UWP.vcxproj @@ -46,67 +46,67 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 PPSSPP_UWP + 10.0.15063.0 Application true - v140 + v141 Application true - v140 + v141 Application true - v140 + v141 Application false true - v140 + v141 true Application false true - v140 + v141 true Application false false - v140 + v141 true Application false false - v140 + v141 true Application false true - v140 + v141 true Application false true - v140 + v141 true diff --git a/UWP/UWPHost.cpp b/UWP/UWPHost.cpp index b7281b95ad..72dd263857 100644 --- a/UWP/UWPHost.cpp +++ b/UWP/UWPHost.cpp @@ -176,10 +176,6 @@ bool UWPHost::CreateDesktopShortcut(std::string argumentPath, std::string gameTi return false; } -void UWPHost::GoFullscreen(bool viewFullscreen) { - // TODO -} - void UWPHost::ToggleDebugConsoleVisibility() { // N/A } diff --git a/UWP/UWPHost.h b/UWP/UWPHost.h index 75353b5cc3..635e37bccc 100644 --- a/UWP/UWPHost.h +++ b/UWP/UWPHost.h @@ -44,8 +44,6 @@ public: void NotifyUserMessage(const std::string &message, float duration = 1.0f, u32 color = 0x00FFFFFF, const char *id = nullptr) override; - void GoFullscreen(bool) override; - GraphicsContext *GetGraphicsContext() { return nullptr; } private: diff --git a/UWP/glslang_UWP/glslang_UWP.vcxproj b/UWP/glslang_UWP/glslang_UWP.vcxproj index 44b73b2814..5673b2a5be 100644 --- a/UWP/glslang_UWP/glslang_UWP.vcxproj +++ b/UWP/glslang_UWP/glslang_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/libkirk_UWP/libkirk_UWP.vcxproj b/UWP/libkirk_UWP/libkirk_UWP.vcxproj index 48113e0c0b..49ecaec065 100644 --- a/UWP/libkirk_UWP/libkirk_UWP.vcxproj +++ b/UWP/libkirk_UWP/libkirk_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/UWP/zlib_UWP/zlib_UWP.vcxproj b/UWP/zlib_UWP/zlib_UWP.vcxproj index 7d858a7cc5..ce7b30285c 100644 --- a/UWP/zlib_UWP/zlib_UWP.vcxproj +++ b/UWP/zlib_UWP/zlib_UWP.vcxproj @@ -46,61 +46,61 @@ 14.0 true Windows Store - 10.0.10586.0 10.0.10240.0 10.0 + 10.0.15063.0 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false false - v140 + v141 StaticLibrary false true - v140 + v141 StaticLibrary false true - v140 + v141 diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index aa96d648ca..56711f541a 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -492,6 +492,7 @@ static const char *glsl_fragment_prelude = "#endif\n"; std::string ApplyGLSLPrelude(const std::string &source, uint32_t stage) { +#if !PPSSPP_PLATFORM(UWP) std::string temp; std::string version = ""; if (!gl_extensions.IsGLES && gl_extensions.IsCoreContext) { @@ -512,4 +513,7 @@ std::string ApplyGLSLPrelude(const std::string &source, uint32_t stage) { temp = version + source; } return temp; +#else + return source; +#endif } From 4350ee153c08000383909ebc0bf65c1e6d039e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Oct 2017 15:12:06 +0200 Subject: [PATCH 010/111] D3D11: Fix a bug, add depth/stencil support to CopyFramebufferToMemorySync --- ext/native/thin3d/thin3d_d3d11.cpp | 62 ++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index d8b83229a8..f890dfb0a3 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -1356,36 +1356,45 @@ void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 wid } } -bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) { +bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat format, void *pixels, int pixelStride) { D3D11Framebuffer *fb = (D3D11Framebuffer *)src; assert(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM); - bool useGlobalPacktex = (x + w <= 512 && y + h <= 512); + bool useGlobalPacktex = (bx + bw <= 512 && by + bh <= 512); ID3D11Texture2D *packTex; if (!useGlobalPacktex) { D3D11_TEXTURE2D_DESC packDesc{}; packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; packDesc.BindFlags = 0; - packDesc.Width = w; - packDesc.Height = h; + packDesc.Width = bw; + packDesc.Height = bh; packDesc.ArraySize = 1; packDesc.MipLevels = 1; packDesc.Usage = D3D11_USAGE_STAGING; packDesc.SampleDesc.Count = 1; - packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - device_->CreateTexture2D(&packDesc, nullptr, &packTex); - - context_->CopyResource(packTex, fb->colorTex); + D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 }; + switch (channelBits) { + case FB_COLOR_BIT: + packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // TODO: fb->colorFormat; + device_->CreateTexture2D(&packDesc, nullptr, &packTex); + context_->CopySubresourceRegion(packTex, 0, bx, by, 0, fb->colorTex, 0, &srcBox); + break; + case FB_DEPTH_BIT: + case FB_STENCIL_BIT: + packDesc.Format = fb->depthStencilFormat; + device_->CreateTexture2D(&packDesc, nullptr, &packTex); + // For depth/stencil buffers, we can't reliably copy subrectangles, so just copy the whole resource. + context_->CopyResource(packTex, fb->colorTex); + break; + default: + assert(false); + } } else { packTex = packTexture_; } - // Only copy the necessary rectangle. - D3D11_BOX srcBox{ (UINT)x, (UINT)y, 0, (UINT)(x + w), (UINT)(y + h), 1 }; - context_->CopySubresourceRegion(packTex, 0, x, y, 0, fb->colorTex, 0, &srcBox); - // Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game // does a once-off copy, that won't work at all. @@ -1396,9 +1405,32 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel return false; } - const int srcByteOffset = y * map.RowPitch + x * 4; - // Pixel size always 4 here because we always request BGRA8888. - ConvertFromRGBA8888((u8 *)pixels, (u8 *)map.pData + srcByteOffset, pixelStride, map.RowPitch / 4, w, h, format); + const int srcByteOffset = by * map.RowPitch + bx * 4; + switch (channelBits) { + case FB_COLOR_BIT: + // Pixel size always 4 here because we always request BGRA8888. + ConvertFromRGBA8888((uint8_t *)pixels, (uint8_t *)map.pData + srcByteOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, format); + break; + case FB_DEPTH_BIT: + for (int y = by; y < by + bh; y++) { + float *dest = (float *)((uint8_t *)pixels + y * pixelStride * sizeof(float)); + const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y); + for (int x = bx; x < bx + bw; x++) { + dest[x] = (src[x] & 0xFFFFFF) / (256.f * 256.f * 256.f); + } + } + break; + case FB_STENCIL_BIT: + for (int y = by; y < by + bh; y++) { + uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride; + const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y); + for (int x = bx; x < bx + bw; x++) { + destStencil[x] = src[x] >> 24; + } + } + break; + } + context_->Unmap(packTex, 0); if (!useGlobalPacktex) { From dd98e877d439c2e7a2d3a3c6d4c2a1d33f5ab177 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Oct 2017 21:45:07 -0700 Subject: [PATCH 011/111] Windows: Fix over/under write for multibyte chars. --- Common/ConsoleListener.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index d899aaf1fd..49d9ce8feb 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -136,6 +136,7 @@ void ConsoleListener::Open() // Set the console window title SetConsoleTitle(title_.c_str()); SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); // Set letter space LetterSpace(openWidth_, LOG_MAX_DISPLAY_LINES); @@ -503,7 +504,7 @@ void ConsoleListener::WriteToConsole(LogTypes::LOG_LEVELS Level, const char *Tex SetConsoleTextAttribute(hConsole, Color); int wlen = MultiByteToWideChar(CP_UTF8, 0, Text, (int)Len, NULL, NULL); MultiByteToWideChar(CP_UTF8, 0, Text, (int)Len, tempBuf, wlen); - WriteConsole(hConsole, tempBuf, (DWORD)Len, &cCharsWritten, NULL); + WriteConsole(hConsole, tempBuf, (DWORD)wlen, &cCharsWritten, NULL); } #endif From 42c0e7d5ddf48f3028b8bbf4a22b14787cc91595 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Oct 2017 21:52:23 -0700 Subject: [PATCH 012/111] Savedata: Handle disk full properly in GETSIZE. Games use these fields to determine how much more space is needed to create savedata. Some also use it for install data. This allows more games to properly report free space issues. --- Core/Dialog/SavedataParam.cpp | 71 ++++++++++++++++++++++++++--------- Core/Dialog/SavedataParam.h | 2 +- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 0640eca06b..ac506478bc 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -903,7 +903,7 @@ int SavedataParam::BuildHash(unsigned char *output, return 0; } -std::string SavedataParam::GetSpaceText(u64 size) +std::string SavedataParam::GetSpaceText(u64 size, bool roundUp) { static const char *suffixes[] = {"B", "KB", "MB", "GB"}; char text[50]; @@ -915,7 +915,11 @@ std::string SavedataParam::GetSpaceText(u64 size) snprintf(text, sizeof(text), "%lld %s", size, suffixes[i]); return std::string(text); } - size /= 1024; + if (roundUp) { + size = (size + 1023) / 1024; + } else { + size /= 1024; + } } snprintf(text, sizeof(text), "%llu TB", size); @@ -936,7 +940,7 @@ int SavedataParam::GetSizes(SceUtilitySavedataParam *param) param->msFree->clusterSize = (u32)MemoryStick_SectorSize(); param->msFree->freeClusters = (u32)(freeBytes / MemoryStick_SectorSize()); param->msFree->freeSpaceKB = (u32)(freeBytes / 0x400); - const std::string spaceTxt = SavedataParam::GetSpaceText(freeBytes); + const std::string spaceTxt = SavedataParam::GetSpaceText(freeBytes, false); memset(param->msFree->freeSpaceStr, 0, sizeof(param->msFree->freeSpaceStr)); strncpy(param->msFree->freeSpaceStr, spaceTxt.c_str(), sizeof(param->msFree->freeSpaceStr)); } @@ -959,7 +963,7 @@ int SavedataParam::GetSizes(SceUtilitySavedataParam *param) // Fieldrunners expects 736 KB, even though the files add up to ~600 KB. int total_size = param->msData->info.usedClusters * (u32)MemoryStick_SectorSize(); param->msData->info.usedSpaceKB = total_size / 0x400; - std::string spaceTxt = SavedataParam::GetSpaceText(total_size); + std::string spaceTxt = SavedataParam::GetSpaceText(total_size, true); strncpy(param->msData->info.usedSpaceStr, spaceTxt.c_str(), sizeof(param->msData->info.usedSpaceStr)); // TODO: What does this mean, then? Seems to be the same. @@ -1001,13 +1005,13 @@ int SavedataParam::GetSizes(SceUtilitySavedataParam *param) param->utilityData->usedClusters = total_size / (u32)MemoryStick_SectorSize(); param->utilityData->usedSpaceKB = total_size / 0x400; - std::string spaceTxt = SavedataParam::GetSpaceText(total_size); + std::string spaceTxt = SavedataParam::GetSpaceText(total_size, true); memset(param->utilityData->usedSpaceStr, 0, sizeof(param->utilityData->usedSpaceStr)); strncpy(param->utilityData->usedSpaceStr, spaceTxt.c_str(), sizeof(param->utilityData->usedSpaceStr)); // TODO: Maybe these are rounded to the nearest 32KB? Or something? param->utilityData->usedSpace32KB = total_size / 0x400; - spaceTxt = SavedataParam::GetSpaceText(total_size); + spaceTxt = SavedataParam::GetSpaceText(total_size, true); memset(param->utilityData->usedSpace32Str, 0, sizeof(param->utilityData->usedSpace32Str)); strncpy(param->utilityData->usedSpace32Str, spaceTxt.c_str(), sizeof(param->utilityData->usedSpace32Str)); } @@ -1188,29 +1192,62 @@ bool SavedataParam::GetSize(SceUtilitySavedataParam *param) return false; } - std::string saveDir = savePath + GetGameName(param) + GetSaveName(param); + const std::string saveDir = savePath + GetGameName(param) + GetSaveName(param); PSPFileInfo info = pspFileSystem.GetFileInfo(saveDir); bool exists = info.exists; if (param->sizeInfo.IsValid()) { const u64 freeBytes = MemoryStick_FreeSpace(); - // TODO: Read the entries and count up the size vs. existing size? + + s64 overwriteBytes = 0; + s64 writeBytes = 0; + for (int i = 0; i < param->sizeInfo->numNormalEntries; ++i) { + const auto &entry = param->sizeInfo->normalEntries[i]; + overwriteBytes += pspFileSystem.GetFileInfo(saveDir + "/" + entry.name).size; + writeBytes += entry.size; + } + for (int i = 0; i < param->sizeInfo->numSecureEntries; ++i) { + const auto &entry = param->sizeInfo->secureEntries[i]; + overwriteBytes += pspFileSystem.GetFileInfo(saveDir + "/" + entry.name).size; + writeBytes += entry.size + 0x10; + } param->sizeInfo->sectorSize = (int)MemoryStick_SectorSize(); param->sizeInfo->freeSectors = (int)(freeBytes / MemoryStick_SectorSize()); - // TODO: Is this after the specified files? Before? + // TODO: Is this after the specified files? Probably before? param->sizeInfo->freeKB = (int)(freeBytes / 1024); - std::string spaceTxt = SavedataParam::GetSpaceText(freeBytes); - strncpy(param->sizeInfo->freeString, spaceTxt.c_str(), 8); - param->sizeInfo->freeString[7] = '\0'; + std::string spaceTxt = SavedataParam::GetSpaceText(freeBytes, false); + truncate_cpy(param->sizeInfo->freeString, spaceTxt.c_str()); - // TODO. - param->sizeInfo->neededKB = 0; - strcpy(param->sizeInfo->neededString, "0 KB"); - param->sizeInfo->overwriteKB = 0; - strcpy(param->sizeInfo->overwriteString, "0 KB"); + if (writeBytes - overwriteBytes < (s64)freeBytes) { + param->sizeInfo->neededKB = 0; + + // Note: this is "needed to overwrite". + param->sizeInfo->overwriteKB = 0; + + spaceTxt = GetSpaceText(0, true); + truncate_cpy(param->sizeInfo->neededString, spaceTxt.c_str()); + truncate_cpy(param->sizeInfo->overwriteString, spaceTxt.c_str()); + } else { + // Bytes needed to save additional data. + s64 neededBytes = writeBytes - freeBytes; + param->sizeInfo->neededKB = (neededBytes + 1023) / 1024; + spaceTxt = GetSpaceText(neededBytes, true); + truncate_cpy(param->sizeInfo->neededString, spaceTxt.c_str()); + + if (writeBytes - overwriteBytes < (s64)freeBytes) { + param->sizeInfo->overwriteKB = 0; + spaceTxt = GetSpaceText(0, true); + truncate_cpy(param->sizeInfo->overwriteString, spaceTxt.c_str()); + } else { + s64 neededOverwriteBytes = writeBytes - freeBytes - overwriteBytes; + param->sizeInfo->overwriteKB = (neededOverwriteBytes + 1023) / 1024; + spaceTxt = GetSpaceText(neededOverwriteBytes, true); + truncate_cpy(param->sizeInfo->overwriteString, spaceTxt.c_str()); + } + } } return exists; diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index be8c679305..cccfbfc108 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -323,7 +323,7 @@ public: std::string GetKey(const SceUtilitySavedataParam *param) const; bool HasKey(const SceUtilitySavedataParam *param) const; - static std::string GetSpaceText(u64 size); + static std::string GetSpaceText(u64 size, bool roundUp); int SetPspParam(SceUtilitySavedataParam* param); SceUtilitySavedataParam *GetPspParam(); From 078e03c7cbcdd927eef413f9fbcb9042795b4e0f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Oct 2017 22:10:05 -0700 Subject: [PATCH 013/111] Gamedata: Fix savestates for data install. Otherwise, with rewind enabled, it hangs forever. --- Core/Dialog/PSPGamedataInstallDialog.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Core/Dialog/PSPGamedataInstallDialog.cpp b/Core/Dialog/PSPGamedataInstallDialog.cpp index a57de30af7..227ae76093 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.cpp +++ b/Core/Dialog/PSPGamedataInstallDialog.cpp @@ -35,6 +35,9 @@ const static u32 GAMEDATA_BYTES_PER_READ = 32768; // If this is too high, some games (e.g. Senjou no Valkyria 3) will lag. const static u32 GAMEDATA_READS_PER_UPDATE = 20; +const u32 ERROR_UTILITY_GAMEDATA_MEMSTRICK_WRITE_PROTECTED = 0x80111903; +const u32 ERROR_UTILITY_GAMEDATA_MEMSTRICK_REMOVED = 0x80111901; + static const std::string SFO_FILENAME = "PARAM.SFO"; namespace @@ -95,6 +98,9 @@ int PSPGamedataInstallDialog::Init(u32 paramAddr) { int PSPGamedataInstallDialog::Update(int animSpeed) { if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) return SCE_ERROR_UTILITY_INVALID_STATUS; + + // TODO: This should return error codes in some cases, like write failure. + // request.common.result must be updated for errors as well. if (readFiles < numFiles) { if (currentInputFile != 0 && currentOutputFile != 0) { @@ -244,7 +250,7 @@ void PSPGamedataInstallDialog::UpdateProgress() { } void PSPGamedataInstallDialog::DoState(PointerWrap &p) { - auto s = p.Section("PSPGamedataInstallDialog", 0, 3); + auto s = p.Section("PSPGamedataInstallDialog", 0, 4); if (!s) return; @@ -252,8 +258,8 @@ void PSPGamedataInstallDialog::DoState(PointerWrap &p) { PSPDialog::DoState(p); p.Do(request); - // This was included in version 2 and higher. - if (s > 2) { + // This was included in version 2 and higher, but for BC reasons we use 3+. + if (s >= 3) { p.Do(param.ptr); p.Do(inFileNames); p.Do(numFiles); @@ -265,7 +271,7 @@ void PSPGamedataInstallDialog::DoState(PointerWrap &p) { param.ptr = 0; } - if (s > 3) { + if (s >= 4) { p.Do(currentInputFile); p.Do(currentInputBytesLeft); p.Do(currentOutputFile); From 190691f249c7ab6a4e1ae529fc76e08417397e2b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Oct 2017 22:53:17 -0700 Subject: [PATCH 014/111] OSK: Limit allowed keyboards based on input mask. --- .editorconfig | 2 +- Core/Dialog/PSPOskDialog.cpp | 118 ++++++++++++++++++++++++++--------- 2 files changed, 88 insertions(+), 32 deletions(-) diff --git a/.editorconfig b/.editorconfig index 607bc73842..21b678c82b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,7 +23,7 @@ indent_style = space indent_size = 4 [Core/Dialog/PSPOskDialog.cpp] -charset = utf-16le +charset = utf-8-bom [ext/native/tools/kanjifilter.h] # This file may need to force a BOM so compilers treat as utf-8. diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index 518ca2797a..cf09c4db42 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -143,6 +143,18 @@ static const wchar_t oskKeys[OSK_KEYBOARD_COUNT][5][14] = }, }; +// This isn't a complete representation of these flags, it just helps ensure we show the right keyboards. +int allowedInputFlagsMap[OSK_KEYBOARD_COUNT] = { + PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT, + PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA, + PSP_UTILITY_OSK_INPUTTYPE_KOREAN, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL, +}; PSPOskDialog::PSPOskDialog() : PSPDialog() { // This can break all kinds of stuff, changing the decimal point in sprintf for example. @@ -227,6 +239,42 @@ void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const wchar_t *input) _string = stringBuffer; } +static void FindValidKeyboard(s32 inputType, int direction, OskKeyboardLanguage &lang, OskKeyboardDisplay &disp) { + OskKeyboardLanguage origLang = lang; + OskKeyboardDisplay origDisp = disp; + + if (inputType == 0) { + return; + } + + // TODO: Limit by allowed keyboards properly... this is just an approximation. + int tries = OSK_LANGUAGE_COUNT * 2; + while (!(inputType & allowedInputFlagsMap[disp]) && tries > 0) { + if ((--tries % 1) == 0) { + lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + lang + direction) % OSK_LANGUAGE_COUNT); + disp = OskKeyboardCases[lang][LOWERCASE]; + } else { + disp = OskKeyboardCases[lang][UPPERCASE]; + } + } + + if (tries == 0) { + // In case of error, let's just fall back to allowing all. + lang = origLang; + disp = origDisp; + } +} + +static bool IsKeyboardShiftValid(s32 inputType, OskKeyboardLanguage lang, OskKeyboardDisplay disp) { + // Swap disp and check if it's valid. + if (disp == OskKeyboardCases[lang][UPPERCASE]) + disp = OskKeyboardCases[lang][LOWERCASE]; + else + disp = OskKeyboardCases[lang][UPPERCASE]; + + return inputType == 0 || (inputType & allowedInputFlagsMap[disp]) != 0; +} + int PSPOskDialog::Init(u32 oskPtr) { // Ignore if already running if (GetStatus() != SCE_UTILITY_STATUS_NONE) { @@ -259,8 +307,9 @@ int PSPOskDialog::Init(u32 oskPtr) { ChangeStatusInit(OSK_INIT_DELAY_US); selectedChar = 0; - currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE; currentKeyboardLanguage = OSK_LANGUAGE_ENGLISH; + currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE; + FindValidKeyboard(oskParams->fields[0].inputtype, 1, currentKeyboardLanguage, currentKeyboard); ConvertUCS2ToUTF8(oskDesc, oskParams->fields[0].desc); ConvertUCS2ToUTF8(oskIntext, oskParams->fields[0].intext); @@ -887,37 +936,43 @@ int PSPOskDialog::Update(int animSpeed) { PPGeDrawText("Start", 135, 220, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); PPGeDrawText(di->T("Finish"), 185, 222, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); - int index = (currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT; - const char *countryCode; + auto lookupLangName = [&](int direction) { + // First, find the valid one... + OskKeyboardLanguage lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + currentKeyboardLanguage + direction) % OSK_LANGUAGE_COUNT); + OskKeyboardDisplay disp = OskKeyboardCases[lang][LOWERCASE]; + FindValidKeyboard(oskParams->fields[0].inputtype, direction, lang, disp); - if (index >= 0) - countryCode = OskKeyboardNames[(currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT].c_str(); - else - countryCode = OskKeyboardNames[OSK_LANGUAGE_COUNT - 1].c_str(); + if (lang == currentKeyboardLanguage) { + return (const char *)nullptr; + } - const char *language = languageMapping[countryCode].first.c_str(); + // Now, let's grab the name. + const char *countryCode = OskKeyboardNames[lang].c_str(); + const char *language = languageMapping[countryCode].first.c_str(); - if (!strcmp(countryCode, "English Full-width")) - language = "English Full-width"; - - countryCode = OskKeyboardNames[currentKeyboardLanguage].c_str(); - - if (strcmp(countryCode, "ko_KR")) { + // It seems like this is a "fake" country code for extra keyboard purposes. + if (!strcmp(countryCode, "English Full-width")) + language = "English Full-width"; + + return language; + }; + + if (OskKeyboardNames[currentKeyboardLanguage] != "ko_KR" && IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) { PPGeDrawText("Select", 135, 245, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); PPGeDrawText(di->T("Shift"), 185, 247, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); } - - PPGeDrawText("L", 235, 220, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText(language, 255, 222, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); - countryCode = OskKeyboardNames[(currentKeyboardLanguage + 1) % OSK_LANGUAGE_COUNT].c_str(); - language = languageMapping[countryCode].first.c_str(); + const char *prevLang = lookupLangName(-1); + if (prevLang) { + PPGeDrawText("L", 235, 220, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText(prevLang, 255, 222, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + } - if (!strcmp(countryCode, "English Full-width")) - language = "English Full-width"; - - PPGeDrawText("R", 235, 245, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); - PPGeDrawText(language, 255, 247, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + const char *nextLang = lookupLangName(1); + if (nextLang) { + PPGeDrawText("R", 235, 245, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawText(nextLang, 255, 247, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF)); + } if (IsButtonPressed(CTRL_UP) || IsButtonHeld(CTRL_UP, upBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { selectedChar -= numKeyCols[currentKeyboard]; @@ -939,10 +994,12 @@ int PSPOskDialog::Update(int animSpeed) { inputChars = CombinationString(true); } else if (IsButtonPressed(CTRL_SELECT)) { // Select now swaps case. - if (currentKeyboard == OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]) - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; - else - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]; + if (IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) { + if (currentKeyboard == OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]) + currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; + else + currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]; + } if (selectedRow >= numKeyRows[currentKeyboard]) { selectedRow = numKeyRows[currentKeyboard] - 1; @@ -954,10 +1011,10 @@ int PSPOskDialog::Update(int animSpeed) { selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; } else if (IsButtonPressed(CTRL_RTRIGGER)) { - // TODO: Limit by allowed keyboards... // RTRIGGER now cycles languages forward. currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage + 1) % OSK_LANGUAGE_COUNT); currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; + FindValidKeyboard(oskParams->fields[0].inputtype, 1, currentKeyboardLanguage, currentKeyboard); if (selectedRow >= numKeyRows[currentKeyboard]) { selectedRow = numKeyRows[currentKeyboard] - 1; @@ -969,14 +1026,13 @@ int PSPOskDialog::Update(int animSpeed) { selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; } else if (IsButtonPressed(CTRL_LTRIGGER)) { - // TODO: Limit by allowed keyboards... // LTRIGGER now cycles languages backward. if (currentKeyboardLanguage - 1 >= 0) currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT); else currentKeyboardLanguage = (OskKeyboardLanguage)(OSK_LANGUAGE_COUNT - 1); - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; + FindValidKeyboard(oskParams->fields[0].inputtype, -1, currentKeyboardLanguage, currentKeyboard); if (selectedRow >= numKeyRows[currentKeyboard]) { selectedRow = numKeyRows[currentKeyboard] - 1; From a37ef3ecbfda2918e6cd9971e0b66946f690ebe8 Mon Sep 17 00:00:00 2001 From: Carlos Estrague / Mrc_munir Date: Sun, 15 Oct 2017 04:33:57 -0400 Subject: [PATCH 015/111] Will fix Android-x86_64 cmake Use the same flags from android-x86 project load fine in marsmallow and nougat tested android-x86 images and avd images set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-fatal-warnings -Wl,-Bsymbolic") --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 558517394e..91f0541b98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1614,7 +1614,7 @@ add_library(${CoreLibName} ${CoreLinkType} if(ANDROID) set(CoreExtraLibs ${CoreExtraLibs} android) if(X86_64) - set(CoreExtraLibs -Wl,--gc-sections -Wl,--exclude-libs,ALL) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-fatal-warnings -Wl,-Bsymbolic") endif() endif() From fc1e8267e7749400cd7729710cf1141fb08468a0 Mon Sep 17 00:00:00 2001 From: Carlos Estrague / Mrc_munir Date: Mon, 16 Oct 2017 00:58:34 +0200 Subject: [PATCH 016/111] restructure some flags x86_64 android seems load with -Wl, only -Bsymbolic --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 91f0541b98..cb3742805d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1614,7 +1614,7 @@ add_library(${CoreLibName} ${CoreLinkType} if(ANDROID) set(CoreExtraLibs ${CoreExtraLibs} android) if(X86_64) - set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-fatal-warnings -Wl,-Bsymbolic") + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-Bsymbolic") endif() endif() From efe251dc10629775c3016366f8f5a8029fab9174 Mon Sep 17 00:00:00 2001 From: Carlos Estrague / Mrc_munir Date: Mon, 16 Oct 2017 01:14:15 +0200 Subject: [PATCH 017/111] Re-enable gradle x86_64 android build It should work now I've tried it with android-x86 Images and with emulator android sdk avd image --- android/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 80455784fd..b49ccd51db 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -58,8 +58,7 @@ android { versionName ANDROID_VERSION_NAME } ndk { - abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86' - // Disabled 'x86_64' due to reported crash issues I can't debug without a device. + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } signingConfig signingConfigs.debug } From ad4bc3f9f4e7b2dd3a5bef4de9fd9f3ded053d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 16 Oct 2017 14:32:26 +0200 Subject: [PATCH 018/111] Address review comments. --- GPU/GLES/FramebufferManagerGLES.cpp | 38 ---------------------------- ext/native/thin3d/thin3d_d3d11.cpp | 24 +++++++++++++----- ext/native/thin3d/thin3d_gl.cpp | 39 +++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 44 deletions(-) diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index aa1665e3b7..7a12932807 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -46,8 +46,6 @@ #include "GPU/GLES/DrawEngineGLES.h" #include "GPU/GLES/ShaderManagerGLES.h" -// #define DEBUG_READ_PIXELS 1 - static const char tex_fs[] = "#if __VERSION__ >= 130\n" "#define varying in\n" @@ -807,42 +805,6 @@ void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u } } -#ifdef DEBUG_READ_PIXELS -// TODO: Make more generic. -static void LogReadPixelsError(GLenum error) { - switch (error) { - case GL_NO_ERROR: - break; - case GL_INVALID_ENUM: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_ENUM"); - break; - case GL_INVALID_VALUE: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_VALUE"); - break; - case GL_INVALID_OPERATION: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_OPERATION"); - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION"); - break; - case GL_OUT_OF_MEMORY: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_OUT_OF_MEMORY"); - break; -#ifndef USING_GLES2 - case GL_STACK_UNDERFLOW: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_UNDERFLOW"); - break; - case GL_STACK_OVERFLOW: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_OVERFLOW"); - break; -#endif - default: - ERROR_LOG(FRAMEBUF, "glReadPixels: %08x", error); - break; - } -} -#endif - void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { CHECK_GL_ERROR_IF_DEBUG(); const int MAX_PBO = 2; diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index f890dfb0a3..067b17f519 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -244,10 +244,12 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de } CreatePresets(); + // Temp texture for read-back of small images. Custom textures are created on demand for larger ones. + // TODO: Should really benchmark if this extra complexity has any benefit. D3D11_TEXTURE2D_DESC packDesc{}; packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; packDesc.BindFlags = 0; - packDesc.Width = 512; // 512x512 is the maximum size of a framebuffer on the PSP. + packDesc.Width = 512; packDesc.Height = 512; packDesc.ArraySize = 1; packDesc.MipLevels = 1; @@ -1378,23 +1380,33 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel switch (channelBits) { case FB_COLOR_BIT: packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // TODO: fb->colorFormat; - device_->CreateTexture2D(&packDesc, nullptr, &packTex); - context_->CopySubresourceRegion(packTex, 0, bx, by, 0, fb->colorTex, 0, &srcBox); break; case FB_DEPTH_BIT: case FB_STENCIL_BIT: packDesc.Format = fb->depthStencilFormat; - device_->CreateTexture2D(&packDesc, nullptr, &packTex); - // For depth/stencil buffers, we can't reliably copy subrectangles, so just copy the whole resource. - context_->CopyResource(packTex, fb->colorTex); break; default: assert(false); } + device_->CreateTexture2D(&packDesc, nullptr, &packTex); } else { packTex = packTexture_; } + D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 }; + switch (channelBits) { + case FB_COLOR_BIT: + context_->CopySubresourceRegion(packTex, 0, bx, by, 0, fb->colorTex, 0, &srcBox); + break; + case FB_DEPTH_BIT: + case FB_STENCIL_BIT: + // For depth/stencil buffers, we can't reliably copy subrectangles, so just copy the whole resource. + context_->CopyResource(packTex, fb->colorTex); + break; + default: + assert(false); + } + // Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game // does a once-off copy, that won't work at all. diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index c48b027085..0d9108286f 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -19,6 +19,8 @@ extern void bindDefaultFBO(); #endif +// #define DEBUG_READ_PIXELS 1 + // Workaround for Retroarch. Simply declare // extern GLuint g_defaultFBO; // and set is as appropriate. Can adjust the variables in ext/native/base/display.h as @@ -870,6 +872,43 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int CHECK_GL_ERROR_IF_DEBUG(); } + +#ifdef DEBUG_READ_PIXELS +// TODO: Make more generic. +static void LogReadPixelsError(GLenum error) { + switch (error) { + case GL_NO_ERROR: + break; + case GL_INVALID_ENUM: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_ENUM"); + break; + case GL_INVALID_VALUE: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_VALUE"); + break; + case GL_INVALID_OPERATION: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_OPERATION"); + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION"); + break; + case GL_OUT_OF_MEMORY: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_OUT_OF_MEMORY"); + break; +#ifndef USING_GLES2 + case GL_STACK_UNDERFLOW: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_UNDERFLOW"); + break; + case GL_STACK_OVERFLOW: + ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_OVERFLOW"); + break; +#endif + default: + ERROR_LOG(FRAMEBUF, "glReadPixels: %08x", error); + break; + } +} +#endif + bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src; fbo_bind_fb_target(true, fb ? fb->handle : 0); From 861c11f70922ad9e1831c5eac0a5744ffddfebbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 16 Oct 2017 14:34:11 +0200 Subject: [PATCH 019/111] Buildfix --- ext/native/thin3d/thin3d_gl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 0d9108286f..d95470d6bc 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -779,12 +779,14 @@ static bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, type = GL_FLOAT; break; +#ifndef USING_GLES2 case DataFormat::S8: internalFormat = GL_STENCIL_INDEX; format = GL_STENCIL_INDEX; type = GL_UNSIGNED_BYTE; alignment = 1; break; +#endif case DataFormat::R8G8B8_UNORM: internalFormat = GL_RGB; From f220c740378f34eb77a1cf5acf6a0046b69a1be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 16 Oct 2017 16:28:27 +0200 Subject: [PATCH 020/111] Remove comment, add check. --- GPU/D3D11/FramebufferManagerD3D11.cpp | 1 - ext/native/thin3d/thin3d_d3d11.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 94cabe7509..7b4ab5efda 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -778,7 +778,6 @@ void FramebufferManagerD3D11::PackFramebufferSync_(VirtualFramebuffer *vfb, int Draw::DataFormat destFormat = GEFormatToThin3D(vfb->format); const int dstBpp = (int)DataFormatSizeInBytes(destFormat); - // TODO: Handle the other formats? We don't currently create them, I think. const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; u8 *destPtr = Memory::GetPointer(fb_address + dstByteOffset); diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 067b17f519..7db2752920 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -1363,7 +1363,7 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel assert(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM); - bool useGlobalPacktex = (bx + bw <= 512 && by + bh <= 512); + bool useGlobalPacktex = (bx + bw <= 512 && by + bh <= 512) && channelBits == FB_COLOR_BIT; ID3D11Texture2D *packTex; if (!useGlobalPacktex) { From 34a61cf57ea3593c60e211a6f1dafc525ea04567 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Mon, 16 Oct 2017 19:07:12 +0100 Subject: [PATCH 021/111] RPI: use new vendor graphics library names Now required to build on recent Raspberry Pi firmware images (Raspbian stretch). --- CMakeLists.txt | 5 ++++- cmake/Toolchains/raspberry.armv6.cmake | 3 ++- cmake/Toolchains/raspberry.armv7.cmake | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb3742805d..2347128fbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -742,7 +742,10 @@ elseif(TARGET SDL2::SDL2) set(nativeExtra ${nativeExtra} SDL/SDLMain.h SDL/SDLMain.mm) set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY}) elseif(USING_EGL) - set(nativeExtraLibs ${nativeExtraLibs} pthread EGL) + if(NOT EGL_LIBRARIES) + set(EGL_LIBRARIES EGL) + endif() + set(nativeExtraLibs ${nativeExtraLibs} pthread ${EGL_LIBRARIES}) endif() set(TargetBin PPSSPPSDL) elseif(WIN32) diff --git a/cmake/Toolchains/raspberry.armv6.cmake b/cmake/Toolchains/raspberry.armv6.cmake index 54182a6a6a..3d93462dab 100644 --- a/cmake/Toolchains/raspberry.armv6.cmake +++ b/cmake/Toolchains/raspberry.armv6.cmake @@ -19,7 +19,8 @@ set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) set(CMAKE_EXE_LINKER_FLAGS "-L/opt/vc/lib" CACHE STRING "" FORCE) -set(OPENGL_LIBRARIES GLESv2 bcm_host) +set(OPENGL_LIBRARIES brcmGLESv2 bcm_host) +set(EGL_LIBRARIES brcmEGL) set(USING_GLES2 ON) set(USING_FBDEV ON) diff --git a/cmake/Toolchains/raspberry.armv7.cmake b/cmake/Toolchains/raspberry.armv7.cmake index 870a3d4489..eaff3d479d 100644 --- a/cmake/Toolchains/raspberry.armv7.cmake +++ b/cmake/Toolchains/raspberry.armv7.cmake @@ -20,7 +20,8 @@ set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) set(CMAKE_EXE_LINKER_FLAGS "-L/opt/vc/lib" CACHE STRING "" FORCE) -set(OPENGL_LIBRARIES GLESv2 bcm_host) +set(OPENGL_LIBRARIES brcmGLESv2 bcm_host) +set(EGL_LIBRARIES brcmEGL) set(USING_GLES2 ON) set(USING_FBDEV ON) set(ARMV7 ON) From f6bf1d5d7cda4c094441ae00edcba7faa51a48a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 17 Oct 2017 15:06:55 +0200 Subject: [PATCH 022/111] Add a controller profile for X360 controllers on RPI, fixes issue #10019 --- assets/gamecontrollerdb.txt | 1 + assets/lang | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/gamecontrollerdb.txt b/assets/gamecontrollerdb.txt index d0be78b895..d05c7b4348 100644 --- a/assets/gamecontrollerdb.txt +++ b/assets/gamecontrollerdb.txt @@ -184,3 +184,4 @@ bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,b 050000004c0500006802000000810000,PLAYSTATION(R)3 Controller,platform:Linux,x:b3,a:b0,b:b1,y:b2,back:b8,guide:b10,start:b9,dpleft:b15,dpdown:b14,dpright:b16,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:a5,leftstick:b7,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4, 03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,platform:Linux,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5, 05000000c82d00002038000000010000,8Bitdo NES30 Pro,platform:Linux,a:b1,b:b0,x:b4,y:b3,back:b10,guide:b2,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4, +030000005e040000a102000000010000,Xbox 360 Wireless Receiver,b:b1,righty:a3,rightx:a2,a:b0,x:b2,righttrigger:b7,rightstick:b12,leftshoulder:b4,dpup:b15,start:b9,dpdown:b16,rightshoulder:b5,dpright:b14,lefttrigger:b6,leftstick:b11,guide:b8,y:b3,lefty:a1,leftx:a0,back:b8,dpleft:b13,platform:Linux, diff --git a/assets/lang b/assets/lang index c31a1d3096..7b31a30fd0 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit c31a1d309631acbe0f2d583f390f163e18befe5d +Subproject commit 7b31a30fd0bbe23b683396e3a8709a8618524341 From 18fc78b34e15220c20a833bcab991f8cc4d18963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 17 Oct 2017 15:07:45 +0200 Subject: [PATCH 023/111] Update lang submodule --- assets/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/lang b/assets/lang index 7b31a30fd0..4fddbc9d30 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 7b31a30fd0bbe23b683396e3a8709a8618524341 +Subproject commit 4fddbc9d3032113bdfed5186de73545e19340b4f From 0a07d1a173461ab6c9ce1ffcc3824935a506772d Mon Sep 17 00:00:00 2001 From: Carlos Estrague / Mrc_munir Date: Wed, 18 Oct 2017 03:47:15 +0200 Subject: [PATCH 024/111] Better path fix x86_64 android Not sure why not needed before when updated my toolchain to NDK15 now no found -landroid and error: cannot open crtbegin_dynamic.o: No such file or directory So we pull it safe in case not detect sysroot properly. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2347128fbc..062d0f411b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1617,7 +1617,7 @@ add_library(${CoreLibName} ${CoreLinkType} if(ANDROID) set(CoreExtraLibs ${CoreExtraLibs} android) if(X86_64) - set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-Bsymbolic") + set(CoreExtraLibs ${CoreExtraLibs} android -Wl,-Bsymbolic") endif() endif() From 52e3406e3d4854af7db5767cd673f2725e8532c5 Mon Sep 17 00:00:00 2001 From: Carlos Estrague / Mrc_munir Date: Wed, 18 Oct 2017 05:37:26 +0200 Subject: [PATCH 025/111] Ops typo --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 062d0f411b..820ac2c889 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1617,7 +1617,7 @@ add_library(${CoreLibName} ${CoreLinkType} if(ANDROID) set(CoreExtraLibs ${CoreExtraLibs} android) if(X86_64) - set(CoreExtraLibs ${CoreExtraLibs} android -Wl,-Bsymbolic") + set(CoreExtraLibs ${CoreExtraLibs} android -Wl,-Bsymbolic) endif() endif() From 214270d19264d97f8a20000ab1c04b2b94408730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Oct 2017 15:21:53 +0200 Subject: [PATCH 026/111] Unify GetDepthBuffer/GetStencilBuffer in GL/D3D11 --- GPU/Common/FramebufferCommon.cpp | 47 +++++++++++++++ GPU/Common/FramebufferCommon.h | 4 +- GPU/D3D11/FramebufferManagerD3D11.cpp | 86 --------------------------- GPU/D3D11/FramebufferManagerD3D11.h | 2 - GPU/GLES/FramebufferManagerGLES.cpp | 49 --------------- GPU/GLES/FramebufferManagerGLES.h | 2 - 6 files changed, 49 insertions(+), 141 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index a49781ee2d..47a0055af0 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1878,3 +1878,50 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB RebindFramebuffer(); return retval; } + +bool FramebufferManagerCommon::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) { + VirtualFramebuffer *vfb = currentRenderVfb_; + if (!vfb) { + vfb = GetVFBAt(fb_address); + } + + if (!vfb) { + // If there's no vfb and we're drawing there, must be memory? + buffer = GPUDebugBuffer(Memory::GetPointer(z_address | 0x04000000), z_stride, 512, GPU_DBG_FORMAT_16BIT); + return true; + } + + if (!vfb->fbo) { + return false; + } + + if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) { + buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT_DIV_256, !useBufferedRendering_); + } else { + buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, !useBufferedRendering_); + } + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::D32F, buffer.GetData(), vfb->renderWidth); + return true; +} + +bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) { + VirtualFramebuffer *vfb = currentRenderVfb_; + if (!vfb) { + vfb = GetVFBAt(fb_address); + } + + if (!vfb) { + // If there's no vfb and we're drawing there, must be memory? + // TODO: Actually get the stencil. + buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, GPU_DBG_FORMAT_8888); + return true; + } + +#ifndef USING_GLES2 + buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, !useBufferedRendering_); + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::S8, buffer.GetData(), vfb->renderWidth); + return true; +#else + return false; +#endif +} diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 0e6ba30a65..ef27b40355 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -279,8 +279,8 @@ public: // Debug features virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes); - virtual bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) = 0; - virtual bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) = 0; + virtual bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer); + virtual bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer); virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer) = 0; protected: diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 7b4ab5efda..a173091d36 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -874,92 +874,6 @@ void FramebufferManagerD3D11::Resized() { CompilePostShader(); } -bool FramebufferManagerD3D11::GetDepthStencilBuffer(VirtualFramebuffer *vfb, GPUDebugBuffer &buffer, bool stencil) { - int w = vfb->renderWidth, h = vfb->renderHeight; - Draw::Framebuffer *fboForRead = nullptr; - fboForRead = vfb->fbo; - - if (stencil) { - buffer.Allocate(w, h, GPU_DBG_FORMAT_8BIT); - } else if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) { - buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT_DIV_256); - } else { - buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT); - } - - ID3D11Texture2D *packTex; - D3D11_TEXTURE2D_DESC packDesc{}; - packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - packDesc.BindFlags = 0; - packDesc.Width = w; - packDesc.Height = h; - packDesc.ArraySize = 1; - packDesc.MipLevels = 1; - packDesc.Usage = D3D11_USAGE_STAGING; - packDesc.SampleDesc.Count = 1; - packDesc.Format = (DXGI_FORMAT)draw_->GetFramebufferAPITexture(fboForRead, Draw::FB_DEPTH_BIT | Draw::FB_FORMAT_BIT, 0); - ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTex)); - - ID3D11Texture2D *nativeTex = (ID3D11Texture2D *)draw_->GetFramebufferAPITexture(fboForRead, Draw::FB_DEPTH_BIT, 0); - context_->CopyResource(packTex, nativeTex); - - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map); - - for (int y = 0; y < h; y++) { - float *dest = (float *)(buffer.GetData() + y * w * 4); - u8 *destStencil = buffer.GetData() + y * w; - const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y); - for (int x = 0; x < w; x++) { - if (stencil) { - destStencil[x] = src[x] >> 24; - } else { - dest[x] = (src[x] & 0xFFFFFF) / (256.f * 256.f * 256.f); - } - } - } - - context_->Unmap(packTex, 0); - packTex->Release(); - return true; -} - -bool FramebufferManagerD3D11::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(z_address | 0x04000000), z_stride, 512, GPU_DBG_FORMAT_16BIT); - return true; - } - - if (!vfb->fbo) { - return false; - } - - return GetDepthStencilBuffer(vfb, buffer, false); -} - -bool FramebufferManagerD3D11::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - return false; - } - - if (!vfb->fbo) { - return false; - } - - return GetDepthStencilBuffer(vfb, buffer, true); -} - bool FramebufferManagerD3D11::GetOutputFramebuffer(GPUDebugBuffer &buffer) { ID3D11Texture2D *backbuffer = (ID3D11Texture2D *)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_COLOR_TEX); if (!backbuffer) { diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index dda9419725..1df4bc4b75 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -67,8 +67,6 @@ public: virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; - bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; virtual void RebindFramebuffer() override; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 7a12932807..b995c3e672 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -1139,52 +1139,3 @@ bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { CHECK_GL_ERROR_IF_DEBUG(); return true; } - -bool FramebufferManagerGLES::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(z_address | 0x04000000), z_stride, 512, GPU_DBG_FORMAT_16BIT); - return true; - } - - if (!vfb->fbo) { - return false; - } - - if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) { - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT_DIV_256, !useBufferedRendering_); - } else { - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, !useBufferedRendering_); - } - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::D32F, buffer.GetData(), vfb->renderWidth); - CHECK_GL_ERROR_IF_DEBUG(); - return true; -} - -bool FramebufferManagerGLES::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) { - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - // TODO: Actually get the stencil. - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, GPU_DBG_FORMAT_8888); - return true; - } - -#ifndef USING_GLES2 - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, !useBufferedRendering_); - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::S8, buffer.GetData(), vfb->renderWidth); - CHECK_GL_ERROR_IF_DEBUG(); - return true; -#else - return false; -#endif -} diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index b860ea92dd..77f58c3afc 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -84,8 +84,6 @@ public: bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; - bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; virtual void RebindFramebuffer() override; From f2ea0ce4722f60183f934c56ce9c0e72c5864577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 16 Oct 2017 16:27:16 +0200 Subject: [PATCH 027/111] Unify (well, almost) GetOutputFramebuffer (D3D11/GL) --- GPU/Common/FramebufferCommon.cpp | 9 ++++++ GPU/Common/FramebufferCommon.h | 2 +- GPU/D3D11/FramebufferManagerD3D11.cpp | 41 --------------------------- GPU/D3D11/FramebufferManagerD3D11.h | 2 -- GPU/GLES/FramebufferManagerGLES.cpp | 11 +++---- GPU/Vulkan/FramebufferVulkan.cpp | 36 ----------------------- ext/native/thin3d/thin3d_d3d11.cpp | 20 +++++++++---- ext/native/thin3d/thin3d_gl.cpp | 9 ++++-- 8 files changed, 36 insertions(+), 94 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 47a0055af0..1f69f6b1f7 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1925,3 +1925,12 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G return false; #endif } + +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; +} + diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index ef27b40355..6de1b6dbf7 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -281,7 +281,7 @@ public: virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes); virtual bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer); virtual bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer); - virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer) = 0; + virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer); protected: virtual void SetViewport2D(int x, int y, int w, int h); diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index a173091d36..546885ee4f 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -873,44 +873,3 @@ void FramebufferManagerD3D11::Resized() { // Might have a new post shader - let's compile it. CompilePostShader(); } - -bool FramebufferManagerD3D11::GetOutputFramebuffer(GPUDebugBuffer &buffer) { - ID3D11Texture2D *backbuffer = (ID3D11Texture2D *)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_COLOR_TEX); - if (!backbuffer) { - ERROR_LOG(G3D, "Failed to get backbuffer from draw context"); - return false; - } - D3D11_TEXTURE2D_DESC desc; - backbuffer->GetDesc(&desc); - int w = desc.Width; - int h = desc.Height; - buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); - - ID3D11Texture2D *packTex; - D3D11_TEXTURE2D_DESC packDesc{}; - packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - packDesc.BindFlags = 0; - packDesc.Width = w; - packDesc.Height = h; - packDesc.ArraySize = 1; - packDesc.MipLevels = 1; - packDesc.Usage = D3D11_USAGE_STAGING; - packDesc.SampleDesc.Count = 1; - packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTex)); - - context_->CopyResource(packTex, backbuffer); - - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map); - - for (int y = 0; y < h; y++) { - uint8_t *dest = (uint8_t *)buffer.GetData() + y * w * 4; - const uint8_t *src = ((const uint8_t *)map.pData) + map.RowPitch * y; - memcpy(dest, src, 4 * w); - } - - context_->Unmap(packTex, 0); - packTex->Release(); - return true; -} diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 1df4bc4b75..cf0798dbf5 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -67,8 +67,6 @@ public: virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; - virtual void RebindFramebuffer() override; // TODO: Remove diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index b995c3e672..832853568e 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -1130,12 +1130,9 @@ void FramebufferManagerGLES::Resized() { } bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { - int pw = PSP_CoreParameter().pixelWidth; - int ph = PSP_CoreParameter().pixelHeight; - - // The backbuffer is flipped (last bool) - buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true); - draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, pw, ph, Draw::DataFormat::R8G8B8_UNORM, buffer.GetData(), pw); - CHECK_GL_ERROR_IF_DEBUG(); + int w, h; + draw_->GetFramebufferDimensions(nullptr, &w, &h); + buffer.Allocate(w, h, GPU_DBG_FORMAT_888_RGB, true); + draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8_UNORM, buffer.GetData(), w); return true; } diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 8eadfa48fa..98ab15627a 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -756,42 +756,6 @@ void ConvertFromRGBA8888_Vulkan(u8 *dst, const u8 *src, u32 dstStride, u32 srcSt } } -#ifdef DEBUG_READ_PIXELS -// TODO: Make more generic. -static void LogReadPixelsError(GLenum error) { - switch (error) { - case GL_NO_ERROR: - break; - case GL_INVALID_ENUM: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_ENUM"); - break; - case GL_INVALID_VALUE: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_VALUE"); - break; - case GL_INVALID_OPERATION: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_OPERATION"); - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION"); - break; - case GL_OUT_OF_MEMORY: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_OUT_OF_MEMORY"); - break; -#ifndef USING_GLES2 - case GL_STACK_UNDERFLOW: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_UNDERFLOW"); - break; - case GL_STACK_OVERFLOW: - ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_OVERFLOW"); - break; -#endif - default: - ERROR_LOG(FRAMEBUF, "glReadPixels: %08x", error); - break; - } -} -#endif - // One frame behind, but no stalling. void FramebufferManagerVulkan::PackFramebufferAsync_(VirtualFramebuffer *vfb) { const int MAX_PBO = 2; diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 7db2752920..ecd7752cc8 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -1361,7 +1361,9 @@ void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 wid bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat format, void *pixels, int pixelStride) { D3D11Framebuffer *fb = (D3D11Framebuffer *)src; - assert(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM); + if (fb) { + assert(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM); + } bool useGlobalPacktex = (bx + bw <= 512 && by + bh <= 512) && channelBits == FB_COLOR_BIT; @@ -1383,6 +1385,7 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel break; case FB_DEPTH_BIT: case FB_STENCIL_BIT: + assert(fb); packDesc.Format = fb->depthStencilFormat; break; default: @@ -1396,15 +1399,17 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 }; switch (channelBits) { case FB_COLOR_BIT: - context_->CopySubresourceRegion(packTex, 0, bx, by, 0, fb->colorTex, 0, &srcBox); + context_->CopySubresourceRegion(packTex, 0, bx, by, 0, fb ? fb->colorTex : bbRenderTargetTex_, 0, &srcBox); break; case FB_DEPTH_BIT: case FB_STENCIL_BIT: // For depth/stencil buffers, we can't reliably copy subrectangles, so just copy the whole resource. - context_->CopyResource(packTex, fb->colorTex); + assert(fb); // We haven't got a texture for the backbuffer depth. Could make one but I don't think we need one. + context_->CopyResource(packTex, fb->depthStencilTex); break; default: assert(false); + break; } // Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game @@ -1509,8 +1514,13 @@ uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int chann void D3D11DrawContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo; - *w = fb->width; - *h = fb->height; + if (fb) { + *w = fb->width; + *h = fb->height; + } else { + *w = bbWidth_; + *h = bbHeight_; + } } DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd) { diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index d95470d6bc..b8dfbb025d 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -1866,8 +1866,13 @@ OpenGLFramebuffer::~OpenGLFramebuffer() { void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; - *w = fb->width; - *h = fb->height; + if (fb) { + *w = fb->width; + *h = fb->height; + } else { + *w = targetWidth_; + *h = targetHeight_; + } } uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const { From 4e3a9bc3e3a22b84cc65183560a9cfd130953608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 18 Oct 2017 11:20:58 +0200 Subject: [PATCH 028/111] Unify some more framebuffer functions. Leave the GL stuff alone for now due to async. --- GPU/Common/FramebufferCommon.cpp | 42 +++++++++++++++++++ GPU/Common/FramebufferCommon.h | 18 ++++++++- GPU/D3D11/FramebufferManagerD3D11.cpp | 58 --------------------------- GPU/D3D11/FramebufferManagerD3D11.h | 3 -- GPU/Directx9/FramebufferDX9.cpp | 18 +-------- GPU/Directx9/FramebufferDX9.h | 3 +- GPU/GLES/FramebufferManagerGLES.cpp | 4 +- GPU/GLES/FramebufferManagerGLES.h | 2 +- GPU/Vulkan/FramebufferVulkan.cpp | 33 --------------- GPU/Vulkan/FramebufferVulkan.h | 5 --- 10 files changed, 65 insertions(+), 121 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 1f69f6b1f7..ac8cd42e35 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1934,3 +1934,45 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { return true; } +// This function takes an already correctly-sized framebuffer and packs it into RAM. +// Does not need to account for scaling. +// Color conversion is currently done on CPU but should be done on GPU. +void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { + if (!vfb->fbo) { + ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferD3D11_: vfb->fbo == 0"); + return; + } + + const u32 fb_address = (0x04000000) | vfb->fb_address; + + Draw::DataFormat destFormat = GEFormatToThin3D(vfb->format); + const int dstBpp = (int)DataFormatSizeInBytes(destFormat); + + const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; + u8 *destPtr = Memory::GetPointer(fb_address + dstByteOffset); + + // We always need to convert from the framebuffer native format. + // Right now that's always 8888. + DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address); + + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride); +} + +void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { + if (vfb) { + // We'll pseudo-blit framebuffers here to get a resized version of vfb. + OptimizeDownloadRange(vfb, x, y, w, h); + if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) { + // No need to blit + PackFramebufferSync_(vfb, x, y, w, h); + } else { + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); + PackFramebufferSync_(nvfb, x, y, w, h); + } + + textureCache_->ForgetLastTexture(); + RebindFramebuffer(); + } +} + diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 6de1b6dbf7..600407f1c8 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -159,6 +159,21 @@ enum DrawTextureFlags { DRAWTEX_KEEP_TEX = 2, }; +inline Draw::DataFormat GEFormatToThin3D(int geFormat) { + switch (geFormat) { + case GE_FORMAT_4444: + return Draw::DataFormat::A4R4G4B4_UNORM_PACK16; + case GE_FORMAT_5551: + return Draw::DataFormat::A1R5G5B5_UNORM_PACK16; + case GE_FORMAT_565: + return Draw::DataFormat::R5G6B5_UNORM_PACK16; + case GE_FORMAT_8888: + return Draw::DataFormat::R8G8B8A8_UNORM; + default: + return Draw::DataFormat::UNDEFINED; + } +} + namespace Draw { class DrawContext; } @@ -210,7 +225,7 @@ public: bool NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason); void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason); - virtual void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) = 0; + virtual void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h); virtual void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) = 0; void DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader); @@ -284,6 +299,7 @@ public: virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer); protected: + virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); virtual void SetViewport2D(int x, int y, int w, int h); void CalculatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight, PostShaderUniforms *uniforms); virtual void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) = 0; diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 546885ee4f..3b47638f02 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -580,25 +580,6 @@ void FramebufferManagerD3D11::BindFramebufferAsColorTexture(int stage, VirtualFr } } -void FramebufferManagerD3D11::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { - if (vfb) { - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - OptimizeDownloadRange(vfb, x, y, w, h); - if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) { - // No need to blit - PackFramebufferSync_(vfb, x, y, w, h); - } - else { - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - PackFramebufferSync_(nvfb, x, y, w, h); - } - - textureCacheD3D11_->ForgetLastTexture(); - RebindFramebuffer(); - } -} - void FramebufferManagerD3D11::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { VirtualFramebuffer *vfb = GetVFBAt(fb_address); if (vfb && vfb->fb_stride != 0) { @@ -749,45 +730,6 @@ void FramebufferManagerD3D11::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, false); } -static Draw::DataFormat GEFormatToThin3D(int geFormat) { - switch (geFormat) { - case GE_FORMAT_4444: - return Draw::DataFormat::A4R4G4B4_UNORM_PACK16; - case GE_FORMAT_5551: - return Draw::DataFormat::A1R5G5B5_UNORM_PACK16; - case GE_FORMAT_565: - return Draw::DataFormat::R5G6B5_UNORM_PACK16; - case GE_FORMAT_8888: - return Draw::DataFormat::R8G8B8A8_UNORM; - default: - return Draw::DataFormat::UNDEFINED; - } -} - -// This function takes an already correctly-sized framebuffer and packs it into RAM. -// Does not need to account for scaling. -// Color conversion is currently done on CPU but should be done on GPU. -void FramebufferManagerD3D11::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (!vfb->fbo) { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferD3D11_: vfb->fbo == 0"); - return; - } - - const u32 fb_address = (0x04000000) | vfb->fb_address; - - Draw::DataFormat destFormat = GEFormatToThin3D(vfb->format); - const int dstBpp = (int)DataFormatSizeInBytes(destFormat); - - const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; - u8 *destPtr = Memory::GetPointer(fb_address + dstByteOffset); - - // We always need to convert from the framebuffer native format. - // Right now that's always 8888. - DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address); - - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride); -} - // Nobody calls this yet. void FramebufferManagerD3D11::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { if (!vfb->fbo) { diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index cf0798dbf5..cdd2093fc7 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -60,7 +60,6 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; std::vector GetFramebufferList(); @@ -89,13 +88,11 @@ private: void BindPostShader(const PostShaderUniforms &uniforms) override; void Bind2DShader() override; void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; - void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); void SimpleBlit( Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, bool linearFilter); - bool GetDepthStencilBuffer(VirtualFramebuffer *vfb, GPUDebugBuffer &buffer, bool stencil); ID3D11Device *device_; ID3D11DeviceContext *context_; diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index d4e4aa1492..8cbbdb9cad 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -473,20 +473,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } } - void FramebufferManagerDX9::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { - if (vfb) { - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - OptimizeDownloadRange(vfb, x, y, w, h); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferDirectx9_(nvfb, x, y, w, h); - - textureCacheDX9_->ForgetLastTexture(); - RebindFramebuffer(); - } - } - void FramebufferManagerDX9::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { VirtualFramebuffer *vfb = GetVFBAt(fb_address); if (vfb && vfb->fb_stride != 0) { @@ -514,7 +500,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - PackFramebufferDirectx9_(nvfb, x, y, w, h); + PackFramebufferSync_(nvfb, x, y, w, h); textureCacheDX9_->ForgetLastTexture(); RebindFramebuffer(); @@ -640,7 +626,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } } - void FramebufferManagerDX9::PackFramebufferDirectx9_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { + void FramebufferManagerDX9::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferDirectx9_: vfb->fbo == 0"); return; diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index f8960a4690..4b03fb0565 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -62,7 +62,6 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; std::vector GetFramebufferList(); @@ -95,7 +94,7 @@ protected: private: void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; - void PackFramebufferDirectx9_(VirtualFramebuffer *vfb, int x, int y, int w, int h); + void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 832853568e..97fa51418c 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -948,9 +948,9 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x int dstByteOffset = y * vfb->fb_stride * dstBpp; u8 *dst = Memory::GetPointer(fb_address + dstByteOffset); - GLubyte *packed = nullptr; + u8 *packed = nullptr; if (!convert) { - packed = (GLubyte *)dst; + packed = (u8 *)dst; } else { // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address if (!convBuf_ || convBufSize_ < bufSize) { diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 77f58c3afc..b02107f48c 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -108,7 +108,7 @@ private: void CompilePostShader(); void PackFramebufferAsync_(VirtualFramebuffer *vfb); // Not used under ES currently - void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); + void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); // Used by DrawPixels diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 98ab15627a..c14705bdc9 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -542,35 +542,6 @@ VulkanTexture *FramebufferManagerVulkan::GetFramebufferColor(u32 fbRawAddress, V } } -void FramebufferManagerVulkan::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { - PROFILE_THIS_SCOPE("gpu-readback"); - if (sync) { - // flush async just in case when we go for synchronous update - // Doesn't actually pack when sent a null argument. - PackFramebufferAsync_(nullptr); - } - - if (vfb) { - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - OptimizeDownloadRange(vfb, x, y, w, h); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - // PackFramebufferSync_() - Synchronous pixel data transfer using glReadPixels - // PackFramebufferAsync_() - Asynchronous pixel data transfer using glReadPixels with PBOs - - // TODO: Can we fall back to sync without these? - if (!sync) { - PackFramebufferAsync_(nvfb); - } else { - PackFramebufferSync_(nvfb, x, y, w, h); - } - - textureCacheVulkan_->ForgetLastTexture(); - RebindFramebuffer(); - } -} - void FramebufferManagerVulkan::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { PROFILE_THIS_SCOPE("gpu-readback"); // Flush async just in case. @@ -874,10 +845,6 @@ void FramebufferManagerVulkan::PackFramebufferAsync_(VirtualFramebuffer *vfb) { currentPBO_ = nextPBO; } -void FramebufferManagerVulkan::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - -} - void FramebufferManagerVulkan::BeginFrameVulkan() { BeginFrame(); diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 8be21bdf76..72a00646c3 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -90,8 +90,6 @@ public: // For use when texturing from a framebuffer. May create a duplicate if target. VulkanTexture *GetFramebufferColor(u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags); - // Reads a rectangular subregion of a framebuffer to the right position in its backing memory. - void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; std::vector GetFramebufferList(); @@ -123,15 +121,12 @@ protected: void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; private: - // The returned texture does not need to be free'd, might be returned from a pool (currently single entry) void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; - void DoNotifyDraw(); void UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight); void PackFramebufferAsync_(VirtualFramebuffer *vfb); - void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); void InitDeviceObjects(); void DestroyDeviceObjects(); From 0d85f86e2d05eefd60761042bf20d9822538bf60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 18 Oct 2017 11:40:07 +0200 Subject: [PATCH 029/111] Add some safety checks. Fix some inadvertent Y-flips (bit hacky). --- GPU/Common/FramebufferCommon.cpp | 48 ++++++++++++++++++++++-------- GPU/Vulkan/FramebufferVulkan.cpp | 2 +- ext/native/thin3d/thin3d_d3d11.cpp | 15 ++++++++-- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index ac8cd42e35..72bbb9f632 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1872,7 +1872,15 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB } } - buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true); + if (!useBufferedRendering_) { + // Safety check. + w = std::min(w, PSP_CoreParameter().pixelWidth); + h = std::min(h, PSP_CoreParameter().pixelHeight); + } + + // TODO: Maybe should handle flipY inside CopyFramebufferToMemorySync somehow? + bool flipY = (g_Config.iGPUBackend == GPU_BACKEND_OPENGL && !useBufferedRendering_) ? true : false; + buffer.Allocate(w, h, GE_FORMAT_8888, flipY, true); bool retval = draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); // We may have blitted to a temp FBO. RebindFramebuffer(); @@ -1891,16 +1899,21 @@ bool FramebufferManagerCommon::GetDepthbuffer(u32 fb_address, int fb_stride, u32 return true; } - if (!vfb->fbo) { - return false; + int w = vfb->renderWidth; + int h = vfb->renderHeight; + if (!useBufferedRendering_) { + // Safety check. + w = std::min(w, PSP_CoreParameter().pixelWidth); + h = std::min(h, PSP_CoreParameter().pixelHeight); } + bool flipY = (g_Config.iGPUBackend == GPU_BACKEND_OPENGL && !useBufferedRendering_) ? true : false; if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) { - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT_DIV_256, !useBufferedRendering_); + buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT_DIV_256, flipY); } else { - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, !useBufferedRendering_); + buffer.Allocate(w, h, GPU_DBG_FORMAT_FLOAT, flipY); } - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::D32F, buffer.GetData(), vfb->renderWidth); + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, w, h, Draw::DataFormat::D32F, buffer.GetData(), w); return true; } @@ -1917,13 +1930,22 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G return true; } -#ifndef USING_GLES2 - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, !useBufferedRendering_); - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::S8, buffer.GetData(), vfb->renderWidth); - return true; -#else - return false; -#endif + int w = vfb->renderWidth; + int h = vfb->renderHeight; + if (!useBufferedRendering_) { + // Safety check. + w = std::min(w, PSP_CoreParameter().pixelWidth); + h = std::min(h, PSP_CoreParameter().pixelHeight); + } + + bool flipY = (g_Config.iGPUBackend == GPU_BACKEND_OPENGL && !useBufferedRendering_) ? true : false; + 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 FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index c14705bdc9..81f67a244d 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -391,7 +391,7 @@ void FramebufferManagerVulkan::RebindFramebuffer() { bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skipZero) { // In Vulkan we should be able to simply copy the stencil data directly to a stencil buffer without - // messing about with bitplane textures and the like. + // messing about with bitplane textures and the like. Or actually, maybe not... return false; } diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index ecd7752cc8..45db38b91a 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -1385,7 +1385,10 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel break; case FB_DEPTH_BIT: case FB_STENCIL_BIT: - assert(fb); + if (!fb) { + // Not supported. + return false; + } packDesc.Format = fb->depthStencilFormat; break; default: @@ -1393,6 +1396,14 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel } device_->CreateTexture2D(&packDesc, nullptr, &packTex); } else { + switch (channelBits) { + case FB_DEPTH_BIT: + case FB_STENCIL_BIT: + if (!fb) + return false; + default: + break; + } packTex = packTexture_; } @@ -1404,7 +1415,7 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel case FB_DEPTH_BIT: case FB_STENCIL_BIT: // For depth/stencil buffers, we can't reliably copy subrectangles, so just copy the whole resource. - assert(fb); // We haven't got a texture for the backbuffer depth. Could make one but I don't think we need one. + assert(fb); // Can't copy depth/stencil from backbuffer. Shouldn't happen thanks to checks above. context_->CopyResource(packTex, fb->depthStencilTex); break; default: From 981c49615cbf139768ba41781570b624bd3cd152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 18 Oct 2017 12:26:02 +0200 Subject: [PATCH 030/111] Unify FlushBeforeCopy. Remove old dummy methods from Vulkan framebuffer manager. --- GPU/Common/FramebufferCommon.cpp | 11 +++ GPU/Common/FramebufferCommon.h | 4 +- GPU/D3D11/FramebufferManagerD3D11.cpp | 16 ++-- GPU/D3D11/FramebufferManagerD3D11.h | 8 +- GPU/Directx9/FramebufferDX9.cpp | 16 ++-- GPU/Directx9/FramebufferDX9.h | 8 +- GPU/GLES/FramebufferManagerGLES.cpp | 21 ++--- GPU/GLES/FramebufferManagerGLES.h | 7 +- GPU/Vulkan/FramebufferVulkan.cpp | 107 ++------------------------ GPU/Vulkan/FramebufferVulkan.h | 12 +-- 10 files changed, 44 insertions(+), 166 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 72bbb9f632..53d1d3bbbc 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -32,6 +32,7 @@ #include "Core/ELF/ParamSFO.h" #include "Core/System.h" #include "Core/HLE/sceDisplay.h" +#include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/FramebufferCommon.h" #include "GPU/Common/PostShader.h" #include "GPU/Common/TextureCacheCommon.h" @@ -1998,3 +1999,13 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, } } +void FramebufferManagerCommon::FlushBeforeCopy() { + // Flush anything not yet drawn before blitting, downloading, or uploading. + // This might be a stalled list, or unflushed before a block transfer, etc. + + // TODO: It's really bad that we are calling SetRenderFramebuffer here with + // all the irrelevant state checking it'll use to decide what to do. Should + // do something more focused here. + SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); + drawEngine_->DispatchFlush(); +} diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 600407f1c8..11c37ca9a3 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -181,6 +181,7 @@ class DrawContext; struct GPUDebugBuffer; class TextureCacheCommon; class ShaderManagerCommon; +class DrawEngineCommon; class FramebufferManagerCommon { public: @@ -314,7 +315,7 @@ protected: void SetNumExtraFBOs(int num); virtual void DisableState() = 0; - virtual void FlushBeforeCopy() = 0; + void FlushBeforeCopy(); virtual void DecimateFBOs(); // keeping it virtual to let D3D do a little extra // Used by ReadFramebufferToMemory and later framebuffer block copies @@ -359,6 +360,7 @@ protected: Draw::DrawContext *draw_ = nullptr; TextureCacheCommon *textureCache_ = nullptr; ShaderManagerCommon *shaderManager_ = nullptr; + DrawEngineCommon *drawEngine_ = nullptr; bool needBackBufferYSwap_ = false; u32 displayFramebufPtr_ = 0; diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 3b47638f02..80f4559736 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -194,6 +194,11 @@ void FramebufferManagerD3D11::SetShaderManager(ShaderManagerD3D11 *sm) { shaderManager_ = sm; } +void FramebufferManagerD3D11::SetDrawEngine(DrawEngineD3D11 *td) { + drawEngineD3D11_ = td; + drawEngine_ = td; +} + void FramebufferManagerD3D11::DisableState() { context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], nullptr, 0xFFFFFFFF); context_->RSSetState(stockD3D11.rasterStateNoCull); @@ -794,17 +799,6 @@ void FramebufferManagerD3D11::DestroyAllFBOs() { DisableState(); } -void FramebufferManagerD3D11::FlushBeforeCopy() { - // Flush anything not yet drawn before blitting, downloading, or uploading. - // This might be a stalled list, or unflushed before a block transfer, etc. - - // TODO: It's really bad that we are calling SetRenderFramebuffer here with - // all the irrelevant state checking it'll use to decide what to do. Should - // do something more focused here. - SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); - drawEngine_->Flush(); -} - void FramebufferManagerD3D11::Resized() { FramebufferManagerCommon::Resized(); diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index cdd2093fc7..0199be4975 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -43,10 +43,7 @@ public: void SetTextureCache(TextureCacheD3D11 *tc); void SetShaderManager(ShaderManagerD3D11 *sm); - void SetDrawEngine(DrawEngineD3D11 *td) { - drawEngine_ = td; - } - + void SetDrawEngine(DrawEngineD3D11 *td); void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; void DestroyAllFBOs(); @@ -75,7 +72,6 @@ public: protected: void DisableState() override; - void FlushBeforeCopy() override; // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; @@ -125,7 +121,7 @@ private: TextureCacheD3D11 *textureCacheD3D11_; ShaderManagerD3D11 *shaderManagerD3D11_; - DrawEngineD3D11 *drawEngine_; + DrawEngineD3D11 *drawEngineD3D11_; // Used by post-processing shader // Postprocessing diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 8cbbdb9cad..036e869fb6 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -155,6 +155,11 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { shaderManager_ = sm; } + void FramebufferManagerDX9::SetDrawEngine(DrawEngineDX9 *td) { + drawEngineD3D9_ = td; + drawEngine_ = td; + } + void FramebufferManagerDX9::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { u8 *convBuf = NULL; D3DLOCKED_RECT rect; @@ -786,17 +791,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { DisableState(); } - void FramebufferManagerDX9::FlushBeforeCopy() { - // Flush anything not yet drawn before blitting, downloading, or uploading. - // This might be a stalled list, or unflushed before a block transfer, etc. - - // TODO: It's really bad that we are calling SetRenderFramebuffer here with - // all the irrelevant state checking it'll use to decide what to do. Should - // do something more focused here. - SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); - drawEngine_->Flush(); - } - void FramebufferManagerDX9::Resized() { FramebufferManagerCommon::Resized(); diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index 4b03fb0565..dc72e01207 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -45,10 +45,7 @@ public: void SetTextureCache(TextureCacheDX9 *tc); void SetShaderManager(ShaderManagerDX9 *sm); - void SetDrawEngine(DrawEngineDX9 *td) { - drawEngine_ = td; - } - + void SetDrawEngine(DrawEngineDX9 *td); void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; void DestroyAllFBOs(); @@ -83,7 +80,6 @@ protected: void BindPostShader(const PostShaderUniforms &uniforms) override; void SetViewport2D(int x, int y, int w, int h) override; void DisableState() override; - void FlushBeforeCopy() override; void DecimateFBOs() override; // Used by ReadFramebufferToMemory and later framebuffer block copies @@ -119,7 +115,7 @@ private: TextureCacheDX9 *textureCacheDX9_; ShaderManagerDX9 *shaderManagerDX9_; - DrawEngineDX9 *drawEngine_; + DrawEngineDX9 *drawEngineD3D9_; struct TempFBO { Draw::Framebuffer *fbo; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 97fa51418c..217f261d85 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -239,6 +239,11 @@ void FramebufferManagerGLES::SetShaderManager(ShaderManagerGLES *sm) { shaderManager_ = sm; } +void FramebufferManagerGLES::SetDrawEngine(DrawEngineGLES *td) { + drawEngineGL_ = td; + drawEngine_ = td; +} + FramebufferManagerGLES::~FramebufferManagerGLES() { if (drawPixelsTex_) glDeleteTextures(1, &drawPixelsTex_); @@ -417,8 +422,8 @@ void FramebufferManagerGLES::DrawActiveTexture(float x, float y, float w, float glEnableVertexAttribArray(program->a_position); glEnableVertexAttribArray(program->a_texcoord0); if (gstate_c.Supports(GPU_SUPPORTS_VAO)) { - drawEngine_->BindBuffer(pos, sizeof(pos), texCoords, sizeof(texCoords)); - drawEngine_->BindElementBuffer(indices, sizeof(indices)); + drawEngineGL_->BindBuffer(pos, sizeof(pos), texCoords, sizeof(texCoords)); + drawEngineGL_->BindElementBuffer(indices, sizeof(indices)); glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0); glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (void *)sizeof(pos)); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0); @@ -1102,18 +1107,6 @@ void FramebufferManagerGLES::DestroyAllFBOs() { CHECK_GL_ERROR_IF_DEBUG(); } -void FramebufferManagerGLES::FlushBeforeCopy() { - // Flush anything not yet drawn before blitting, downloading, or uploading. - // This might be a stalled list, or unflushed before a block transfer, etc. - - // TODO: It's really bad that we are calling SetRenderFramebuffer here with - // all the irrelevant state checking it'll use to decide what to do. Should - // do something more focused here. - SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); - drawEngine_->Flush(); - CHECK_GL_ERROR_IF_DEBUG(); -} - void FramebufferManagerGLES::Resized() { FramebufferManagerCommon::Resized(); diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index b02107f48c..5eb4ccda87 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -55,9 +55,7 @@ public: void SetTextureCache(TextureCacheGLES *tc); void SetShaderManager(ShaderManagerGLES *sm); - void SetDrawEngine(DrawEngineGLES *td) { - drawEngine_ = td; - } + void SetDrawEngine(DrawEngineGLES *td); // x,y,w,h are relative to destW, destH which fill out the target completely. void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; @@ -91,7 +89,6 @@ public: protected: void SetViewport2D(int x, int y, int w, int h) override; void DisableState() override; - void FlushBeforeCopy() override; // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; @@ -131,7 +128,7 @@ private: TextureCacheGLES *textureCacheGL_; ShaderManagerGLES *shaderManagerGL_; - DrawEngineGLES *drawEngine_; + DrawEngineGLES *drawEngineGL_; // Not used under ES currently. AsyncPBO *pixelBufObj_; //this isn't that large diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 81f67a244d..65020ab574 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -113,6 +113,11 @@ void FramebufferManagerVulkan::SetShaderManager(ShaderManagerVulkan *sm) { shaderManager_ = sm; } +void FramebufferManagerVulkan::SetDrawEngine(DrawEngineVulkan *td) { + drawEngineVulkan_ = td; + drawEngine_ = td; +} + void FramebufferManagerVulkan::InitDeviceObjects() { // Initialize framedata for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { @@ -937,19 +942,6 @@ void FramebufferManagerVulkan::DestroyAllFBOs() { bvfbs_.clear(); } -void FramebufferManagerVulkan::FlushBeforeCopy() { - // Flush anything not yet drawn before blitting, downloading, or uploading. - // This might be a stalled list, or unflushed before a block transfer, etc. - - // TODO: It's really bad that we are calling SetRenderFramebuffer here with - // all the irrelevant state checking it'll use to decide what to do. Should - // do something more focused here. - SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); - if (!draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS)) - Crash(); - drawEngine_->Flush(); -} - void FramebufferManagerVulkan::Resized() { FramebufferManagerCommon::Resized(); @@ -957,92 +949,3 @@ void FramebufferManagerVulkan::Resized() { DestroyAllFBOs(); } } - -bool FramebufferManagerVulkan::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxStride) { - // TODO: Doing this synchronously will require stalling the pipeline. Maybe better - // to do it callback-style? -/* - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, format); - return true; - } - - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GE_FORMAT_8888, false, true); - if (vfb->fbo) - fbo_bind_for_read(vfb->fbo); - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) - glReadBuffer(GL_COLOR_ATTACHMENT0); - - glPixelStorei(GL_PACK_ALIGNMENT, 4); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); - */ - return false; -} - -bool FramebufferManagerVulkan::GetOutputFramebuffer(GPUDebugBuffer &buffer) { - // TODO: Doing this synchronously will require stalling the pipeline. Maybe better - // to do it callback-style? - /* - fbo_unbind_read(); - - int pw = PSP_CoreParameter().pixelWidth; - int ph = PSP_CoreParameter().pixelHeight; - - // The backbuffer is flipped. - buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true); - glPixelStorei(GL_PACK_ALIGNMENT, 1); - SafeGLReadPixels(0, 0, pw, ph, GL_RGB, GL_UNSIGNED_BYTE, buffer.GetData()); - */ - return false; -} - -bool FramebufferManagerVulkan::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) { - // TODO: Doing this synchronously will require stalling the pipeline. Maybe better - // to do it callback-style? - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - buffer = GPUDebugBuffer(Memory::GetPointer(z_address | 0x04000000), z_stride, 512, GPU_DBG_FORMAT_16BIT); - return true; - } - - /* - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, false); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_DEPTH_COMPONENT, GL_FLOAT, buffer.GetData()); - */ - return false; -} - -bool FramebufferManagerVulkan::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) { - // TODO: Doing this synchronously will require stalling the pipeline. Maybe better - // to do it callback-style? - VirtualFramebuffer *vfb = currentRenderVfb_; - if (!vfb) { - vfb = GetVFBAt(fb_address); - } - - if (!vfb) { - // If there's no vfb and we're drawing there, must be memory? - // TODO: Actually get the stencil. - buffer = GPUDebugBuffer(Memory::GetPointer(fb_address | 0x04000000), fb_stride, 512, GPU_DBG_FORMAT_8888); - return true; - } - - /* - buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, false); - SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, buffer.GetData()); - - return true; - */ - return false; -} diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 72a00646c3..32770848d2 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -65,9 +65,7 @@ public: void SetTextureCache(TextureCacheVulkan *tc); void SetShaderManager(ShaderManagerVulkan *sm); - void SetDrawEngine(DrawEngineVulkan *td) { - drawEngine_ = td; - } + void SetDrawEngine(DrawEngineVulkan *td); // x,y,w,h are relative to destW, destH which fill out the target completely. void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; @@ -96,11 +94,6 @@ public: bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; - bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) override; - bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override; - bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override; - bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; - virtual void RebindFramebuffer() override; VkImageView BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); @@ -113,7 +106,6 @@ protected: void BindPostShader(const PostShaderUniforms &uniforms) override; void SetViewport2D(int x, int y, int w, int h) override; void DisableState() override {} - void FlushBeforeCopy() override; // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) override; @@ -144,7 +136,7 @@ private: TextureCacheVulkan *textureCacheVulkan_; ShaderManagerVulkan *shaderManagerVulkan_; - DrawEngineVulkan *drawEngine_; + DrawEngineVulkan *drawEngineVulkan_; AsyncPBOVulkan *pixelBufObj_; int currentPBO_; From 17576d78940614e6683a394cc08a097fc1672981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 18 Oct 2017 12:27:58 +0200 Subject: [PATCH 031/111] Remove more dummy code from Vulkan backend. --- GPU/Vulkan/FramebufferVulkan.cpp | 124 ------------------------------- GPU/Vulkan/FramebufferVulkan.h | 19 ----- 2 files changed, 143 deletions(-) diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 65020ab574..a14c203857 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -87,8 +87,6 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk convBufSize_(0), textureCacheVulkan_(nullptr), shaderManagerVulkan_(nullptr), - pixelBufObj_(nullptr), - currentPBO_(0), curFrame_(0), pipelinePostShader_(VK_NULL_HANDLE), vulkan2D_(vulkan) { @@ -549,8 +547,6 @@ VulkanTexture *FramebufferManagerVulkan::GetFramebufferColor(u32 fbRawAddress, V void FramebufferManagerVulkan::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { PROFILE_THIS_SCOPE("gpu-readback"); - // Flush async just in case. - PackFramebufferAsync_(nullptr); VirtualFramebuffer *vfb = GetVFBAt(fb_address); if (vfb && vfb->fb_stride != 0) { @@ -732,124 +728,6 @@ void ConvertFromRGBA8888_Vulkan(u8 *dst, const u8 *src, u32 dstStride, u32 srcSt } } -// One frame behind, but no stalling. -void FramebufferManagerVulkan::PackFramebufferAsync_(VirtualFramebuffer *vfb) { - const int MAX_PBO = 2; - uint8_t *packed = 0; - const u8 nextPBO = (currentPBO_ + 1) % MAX_PBO; - - bool useCPU = false; - - // We'll prepare two PBOs to switch between readying and reading - if (!pixelBufObj_) { - if (!vfb) { - // This call is just to flush the buffers. We don't have any yet, - // so there's nothing to do. - return; - } - - // GLuint pbos[MAX_PBO]; - // glGenBuffers(MAX_PBO, pbos); - - pixelBufObj_ = new AsyncPBOVulkan[MAX_PBO]; - for (int i = 0; i < MAX_PBO; i++) { - // TODO - // pixelBufObj_[i].handle = pbos[i]; - pixelBufObj_[i].maxSize = 0; - pixelBufObj_[i].reading = false; - } - } - - // Receive previously requested data from a PBO - AsyncPBOVulkan &pbo = pixelBufObj_[nextPBO]; - if (pbo.reading) { - // glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle); - // packed = (GLubyte *)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, pbo.size, GL_MAP_READ_BIT); - - if (packed) { - DEBUG_LOG(FRAMEBUF, "Reading PBO to memory , bufSize = %u, packed = %p, fb_address = %08x, stride = %u, pbo = %u", - pbo.size, packed, pbo.fb_address, pbo.stride, nextPBO); - - // We don't need to convert, GPU already did (or should have) - // (vulkan: hopefully) - Memory::MemcpyUnchecked(pbo.fb_address, packed, pbo.size); - - pbo.reading = false; - } - - // glUnmapBuffer(GL_PIXEL_PACK_BUFFER); - } - - // Order packing/readback of the framebuffer - if (vfb) { - // int pixelType, pixelFormat; - int pixelSize, align; - switch (vfb->format) { - case GE_FORMAT_4444: // 16 bit RGBA - // pixelType = GL_UNSIGNED_SHORT_4_4_4_4; - // pixelFormat = GL_RGBA; - pixelSize = 2; - align = 2; - break; - case GE_FORMAT_5551: // 16 bit RGBA - // pixelType = GL_UNSIGNED_SHORT_5_5_5_1; - // pixelFormat = GL_RGBA; - pixelSize = 2; - align = 2; - break; - case GE_FORMAT_565: // 16 bit RGB - // pixelType = GL_UNSIGNED_SHORT_5_6_5; - // pixelFormat = GL_RGB; - pixelSize = 2; - align = 2; - break; - case GE_FORMAT_8888: // 32 bit RGBA - default: - // pixelType = GL_UNSIGNED_BYTE; - // pixelFormat = UseBGRA8888() ? GL_BGRA_EXT : GL_RGBA; - pixelSize = 4; - align = 4; - break; - } - - // If using the CPU, we need 4 bytes per pixel always. - u32 bufSize = vfb->fb_stride * vfb->height * 4 * (useCPU ? 4 : pixelSize); - u32 fb_address = (0x04000000) | vfb->fb_address; - - if (vfb->fbo) { - // fbo_bind_for_read(vfb->fbo); - } else { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferAsync_: vfb->fbo == 0"); - // fbo_unbind_read(); - return; - } - - // glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBufObj_[currentPBO_].handle); - if (pixelBufObj_[currentPBO_].maxSize < bufSize) { - // We reserve a buffer big enough to fit all those pixels - // glBufferData(GL_PIXEL_PACK_BUFFER, bufSize, NULL, GL_DYNAMIC_READ); - pixelBufObj_[currentPBO_].maxSize = bufSize; - } - - if (useCPU) { - // If converting pixel formats on the CPU we'll always request RGBA8888 - // SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, UseBGRA8888() ? GL_BGRA_EXT : GL_RGBA, GL_UNSIGNED_BYTE, 0); - } else { - // Otherwise we'll directly request the format we need and let the GPU sort it out - // SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, pixelFormat, pixelType, 0); - } - - pixelBufObj_[currentPBO_].fb_address = fb_address; - pixelBufObj_[currentPBO_].size = bufSize; - pixelBufObj_[currentPBO_].stride = vfb->fb_stride; - pixelBufObj_[currentPBO_].height = vfb->height; - pixelBufObj_[currentPBO_].format = vfb->format; - pixelBufObj_[currentPBO_].reading = true; - } - - currentPBO_ = nextPBO; -} - void FramebufferManagerVulkan::BeginFrameVulkan() { BeginFrame(); @@ -876,8 +754,6 @@ void FramebufferManagerVulkan::BeginFrameVulkan() { void FramebufferManagerVulkan::EndFrame() { // We flush to memory last requested framebuffer, if any. // Only do this in the read-framebuffer modes. - if (updateVRAM_) - PackFramebufferAsync_(nullptr); FrameData &frame = frameData_[curFrame_]; frame.push_->End(); diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 32770848d2..a24206abc2 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -44,20 +44,6 @@ R"( vec2 texelDelta; vec4 time; )"; -// Simple struct for asynchronous PBO readbacks -// TODO: Probably will need a complete redesign. -struct AsyncPBOVulkan { - // handle; - u32 maxSize; - - u32 fb_address; - u32 stride; - u32 height; - u32 size; - GEBufferFormat format; - bool reading; -}; - class FramebufferManagerVulkan : public FramebufferManagerCommon { public: FramebufferManagerVulkan(Draw::DrawContext *draw, VulkanContext *vulkan); @@ -118,8 +104,6 @@ private: void UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight); - void PackFramebufferAsync_(VirtualFramebuffer *vfb); - void InitDeviceObjects(); void DestroyDeviceObjects(); @@ -138,9 +122,6 @@ private: ShaderManagerVulkan *shaderManagerVulkan_; DrawEngineVulkan *drawEngineVulkan_; - AsyncPBOVulkan *pixelBufObj_; - int currentPBO_; - enum { MAX_COMMAND_BUFFERS = 32, }; From 898716cc268a31255bc168391824fd562e2a0f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 18 Oct 2017 12:34:01 +0200 Subject: [PATCH 032/111] Unify DownloadFramebufferForClut --- GPU/Common/FramebufferCommon.cpp | 35 ++++++++++ GPU/Common/FramebufferCommon.h | 3 +- GPU/D3D11/FramebufferManagerD3D11.cpp | 35 ---------- GPU/D3D11/FramebufferManagerD3D11.h | 2 - GPU/Directx9/FramebufferDX9.cpp | 35 ---------- GPU/Directx9/FramebufferDX9.h | 2 - GPU/GLES/FramebufferManagerGLES.cpp | 34 +--------- GPU/Vulkan/FramebufferVulkan.cpp | 92 --------------------------- GPU/Vulkan/FramebufferVulkan.h | 5 -- 9 files changed, 38 insertions(+), 205 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 53d1d3bbbc..4c834a732b 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -2009,3 +2009,38 @@ void FramebufferManagerCommon::FlushBeforeCopy() { SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); drawEngine_->DispatchFlush(); } + +void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { + VirtualFramebuffer *vfb = GetVFBAt(fb_address); + if (vfb && vfb->fb_stride != 0) { + const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; + int x = 0; + int y = 0; + int pixels = loadBytes / bpp; + // The height will be 1 for each stride or part thereof. + int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); + int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); + + // We might still have a pending draw to the fb in question, flush if so. + FlushBeforeCopy(); + + // No need to download if we already have it. + if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { + // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. + // CLUT framebuffers are often incorrectly estimated in size. + if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { + vfb->memoryUpdated = true; + } + vfb->clutUpdatedBytes = loadBytes; + + // We'll pseudo-blit framebuffers here to get a resized version of vfb. + VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); + BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); + + PackFramebufferSync_(nvfb, x, y, w, h); + + textureCache_->ForgetLastTexture(); + RebindFramebuffer(); + } + } +} diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 11c37ca9a3..c108d5fd23 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -227,7 +227,8 @@ public: void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason); virtual void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h); - virtual void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) = 0; + + virtual void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes); void DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader); void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height); diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 80f4559736..8d0ed94233 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -585,41 +585,6 @@ void FramebufferManagerD3D11::BindFramebufferAsColorTexture(int stage, VirtualFr } } -void FramebufferManagerD3D11::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { - VirtualFramebuffer *vfb = GetVFBAt(fb_address); - if (vfb && vfb->fb_stride != 0) { - const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; - int x = 0; - int y = 0; - int pixels = loadBytes / bpp; - // The height will be 1 for each stride or part thereof. - int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); - int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); - - // We might still have a pending draw to the fb in question, flush if so. - FlushBeforeCopy(); - - // No need to download if we already have it. - if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { - // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. - // CLUT framebuffers are often incorrectly estimated in size. - if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { - vfb->memoryUpdated = true; - } - vfb->clutUpdatedBytes = loadBytes; - - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferSync_(nvfb, x, y, w, h); - - textureCacheD3D11_->ForgetLastTexture(); - RebindFramebuffer(); - } - } -} - bool FramebufferManagerD3D11::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { nvfb->colorDepth = Draw::FBO_8888; diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 0199be4975..ec5a0fb839 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -57,8 +57,6 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; - std::vector GetFramebufferList(); virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 036e869fb6..a5796552da 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -478,41 +478,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } } - void FramebufferManagerDX9::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { - VirtualFramebuffer *vfb = GetVFBAt(fb_address); - if (vfb && vfb->fb_stride != 0) { - const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; - int x = 0; - int y = 0; - int pixels = loadBytes / bpp; - // The height will be 1 for each stride or part thereof. - int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); - int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); - - // We might still have a pending draw to the fb in question, flush if so. - FlushBeforeCopy(); - - // No need to download if we already have it. - if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { - // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. - // CLUT framebuffers are often incorrectly estimated in size. - if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { - vfb->memoryUpdated = true; - } - vfb->clutUpdatedBytes = loadBytes; - - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferSync_(nvfb, x, y, w, h); - - textureCacheDX9_->ForgetLastTexture(); - RebindFramebuffer(); - } - } - } - bool FramebufferManagerDX9::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { nvfb->colorDepth = Draw::FBO_8888; diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index dc72e01207..a8e4768380 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -59,8 +59,6 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; - std::vector GetFramebufferList(); virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 217f261d85..e622264eca 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -576,39 +576,7 @@ void FramebufferManagerGLES::DownloadFramebufferForClut(u32 fb_address, u32 load PROFILE_THIS_SCOPE("gpu-readback"); // Flush async just in case. PackFramebufferAsync_(nullptr); - - VirtualFramebuffer *vfb = GetVFBAt(fb_address); - if (vfb && vfb->fb_stride != 0) { - const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; - int x = 0; - int y = 0; - int pixels = loadBytes / bpp; - // The height will be 1 for each stride or part thereof. - int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); - int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); - - // We might still have a pending draw to the fb in question, flush if so. - FlushBeforeCopy(); - - // No need to download if we already have it. - if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { - // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. - // CLUT framebuffers are often incorrectly estimated in size. - if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { - vfb->memoryUpdated = true; - } - vfb->clutUpdatedBytes = loadBytes; - - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferSync_(nvfb, x, y, w, h); - - textureCacheGL_->ForgetLastTexture(); - RebindFramebuffer(); - } - } + FramebufferManagerCommon::DownloadFramebufferForClut(fb_address, loadBytes); } bool FramebufferManagerGLES::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index a14c203857..57bab8361a 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -487,98 +487,6 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V } } -VulkanTexture *FramebufferManagerVulkan::GetFramebufferColor(u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags) { - if (framebuffer == NULL) { - framebuffer = currentRenderVfb_; - } - - if (!framebuffer->fbo || !useBufferedRendering_) { - gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE; - return nullptr; - } - - // currentRenderVfb_ will always be set when this is called, except from the GE debugger. - // Let's just not bother with the copy in that case. - bool skipCopy = (flags & BINDFBCOLOR_MAY_COPY) == 0; - if (GPUStepping::IsStepping() || g_Config.bDisableSlowFramebufEffects) { - skipCopy = true; - } - if (!skipCopy && currentRenderVfb_ && framebuffer->fb_address == fbRawAddress) { - // TODO: Enable the below code - return nullptr; // framebuffer->fbo->GetColor(); - /* - // TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size. - VulkanFBO *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (FBOColorDepth)framebuffer->colorDepth); - if (renderCopy) { - VirtualFramebuffer copyInfo = *framebuffer; - copyInfo.fbo = renderCopy; - - int x = 0; - int y = 0; - int w = framebuffer->drawnWidth; - int h = framebuffer->drawnHeight; - - // If max is not > min, we probably could not detect it. Skip. - // See the vertex decoder, where this is updated. - if ((flags & BINDFBCOLOR_MAY_COPY_WITH_UV) == BINDFBCOLOR_MAY_COPY_WITH_UV && gstate_c.vertBounds.maxU > gstate_c.vertBounds.minU) { - x = gstate_c.vertBounds.minU; - y = gstate_c.vertBounds.minV; - w = gstate_c.vertBounds.maxU - x; - h = gstate_c.vertBounds.maxV - y; - - // If we bound a framebuffer, apply the byte offset as pixels to the copy too. - if (flags & BINDFBCOLOR_APPLY_TEX_OFFSET) { - x += gstate_c.curTextureXOffset; - y += gstate_c.curTextureYOffset; - } - } - - BlitFramebuffer(©Info, x, y, framebuffer, x, y, w, h, 0); - - return nullptr; // fbo_bind_color_as_texture(renderCopy, 0); - } else { - return framebuffer->fbo->GetColor(); - } - */ - } else { - return nullptr; // framebuffer->fbo->GetColor(); - } -} - -void FramebufferManagerVulkan::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) { - PROFILE_THIS_SCOPE("gpu-readback"); - - VirtualFramebuffer *vfb = GetVFBAt(fb_address); - if (vfb && vfb->fb_stride != 0) { - const u32 bpp = vfb->drawnFormat == GE_FORMAT_8888 ? 4 : 2; - int x = 0; - int y = 0; - int pixels = loadBytes / bpp; - // The height will be 1 for each stride or part thereof. - int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); - int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); - - // No need to download if we already have it. - if (!vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) { - // We intentionally don't call OptimizeDownloadRange() here - we don't want to over download. - // CLUT framebuffers are often incorrectly estimated in size. - if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) { - vfb->memoryUpdated = true; - } - vfb->clutUpdatedBytes = loadBytes; - - // We'll pseudo-blit framebuffers here to get a resized version of vfb. - VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb); - BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0); - - PackFramebufferSync_(nvfb, x, y, w, h); - - textureCacheVulkan_->ForgetLastTexture(); - RebindFramebuffer(); - } - } -} - bool FramebufferManagerVulkan::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { // When updating VRAM, it need to be exact format. if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) { diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index a24206abc2..ee78b5cfdc 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -71,11 +71,6 @@ public: void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) override; - // For use when texturing from a framebuffer. May create a duplicate if target. - VulkanTexture *GetFramebufferColor(u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags); - - void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; - std::vector GetFramebufferList(); bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; From d53c88456a94c3f3aa8d5b3f482713a39af97677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 18 Oct 2017 12:49:15 +0200 Subject: [PATCH 033/111] Unify GetFramebufferList() (trivial) --- GPU/Common/FramebufferCommon.cpp | 19 +++++++++++++++++++ GPU/Common/FramebufferCommon.h | 2 ++ GPU/D3D11/FramebufferManagerD3D11.cpp | 19 ------------------- GPU/D3D11/FramebufferManagerD3D11.h | 2 -- GPU/D3D11/GPU_D3D11.cpp | 4 ---- GPU/D3D11/GPU_D3D11.h | 1 - GPU/Directx9/FramebufferDX9.cpp | 19 ------------------- GPU/Directx9/FramebufferDX9.h | 2 -- GPU/Directx9/GPU_DX9.cpp | 4 ---- GPU/Directx9/GPU_DX9.h | 1 - GPU/GLES/GPU_GLES.cpp | 4 ---- GPU/GLES/GPU_GLES.h | 1 - GPU/GPUCommon.cpp | 5 +++++ GPU/GPUCommon.h | 4 +--- GPU/Vulkan/FramebufferVulkan.cpp | 19 ------------------- GPU/Vulkan/FramebufferVulkan.h | 2 -- GPU/Vulkan/GPU_Vulkan.cpp | 4 ---- GPU/Vulkan/GPU_Vulkan.h | 1 - 18 files changed, 27 insertions(+), 86 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 4c834a732b..99f2632baf 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -2044,3 +2044,22 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo } } } + +std::vector FramebufferManagerCommon::GetFramebufferList() { + std::vector list; + + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + + FramebufferInfo info; + info.fb_address = vfb->fb_address; + info.z_address = vfb->z_address; + info.format = vfb->format; + info.width = vfb->width; + info.height = vfb->height; + info.fbo = vfb->fbo; + list.push_back(info); + } + + return list; +} diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index c108d5fd23..2fd39d9a0d 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -25,6 +25,7 @@ #include "Core/MemMap.h" #include "GPU/GPU.h" #include "GPU/ge_constants.h" +#include "GPU/GPUInterface.h" #include "thin3d/thin3d.h" enum { @@ -212,6 +213,7 @@ public: } } virtual void RebindFramebuffer() = 0; + std::vector GetFramebufferList(); void CopyDisplayToOutput(); diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 8d0ed94233..399ac87508 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -718,25 +718,6 @@ void FramebufferManagerD3D11::DeviceLost() { DestroyAllFBOs(); } -std::vector FramebufferManagerD3D11::GetFramebufferList() { - std::vector list; - - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - - FramebufferInfo info; - info.fb_address = vfb->fb_address; - info.z_address = vfb->z_address; - info.format = vfb->format; - info.width = vfb->width; - info.height = vfb->height; - info.fbo = vfb->fbo; - list.push_back(info); - } - - return list; -} - void FramebufferManagerD3D11::DestroyAllFBOs() { currentRenderVfb_ = 0; displayFramebuf_ = 0; diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index ec5a0fb839..508e995e52 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -57,8 +57,6 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - std::vector GetFramebufferList(); - virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; virtual void RebindFramebuffer() override; diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 6874d43759..1b29b1ce4d 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -755,10 +755,6 @@ void GPU_D3D11::ClearShaderCache() { shaderManagerD3D11_->ClearShaders(); } -std::vector GPU_D3D11::GetFramebufferList() { - return framebufferManagerD3D11_->GetFramebufferList(); -} - void GPU_D3D11::DoState(PointerWrap &p) { GPUCommon::DoState(p); diff --git a/GPU/D3D11/GPU_D3D11.h b/GPU/D3D11/GPU_D3D11.h index 6785693bf0..aa94cd13ee 100644 --- a/GPU/D3D11/GPU_D3D11.h +++ b/GPU/D3D11/GPU_D3D11.h @@ -60,7 +60,6 @@ public: primaryInfo = reportingPrimaryInfo_; fullInfo = reportingFullInfo_; } - std::vector GetFramebufferList() override; bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; bool GetCurrentClut(GPUDebugBuffer &buffer) override; diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index a5796552da..c5b319d950 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -693,25 +693,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { DestroyAllFBOs(); } - std::vector FramebufferManagerDX9::GetFramebufferList() { - std::vector list; - - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - - FramebufferInfo info; - info.fb_address = vfb->fb_address; - info.z_address = vfb->z_address; - info.format = vfb->format; - info.width = vfb->width; - info.height = vfb->height; - info.fbo = vfb->fbo; - list.push_back(info); - } - - return list; - } - void FramebufferManagerDX9::DecimateFBOs() { FramebufferManagerCommon::DecimateFBOs(); for (auto it = offscreenSurfaces_.begin(); it != offscreenSurfaces_.end(); ) { diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index a8e4768380..544438ed39 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -59,8 +59,6 @@ public: void BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); - std::vector GetFramebufferList(); - virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes); diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 48c51cfe97..ac2f7930a6 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -688,10 +688,6 @@ void GPU_DX9::ClearShaderCache() { shaderManagerDX9_->ClearCache(true); } -std::vector GPU_DX9::GetFramebufferList() { - return framebufferManagerDX9_->GetFramebufferList(); -} - void GPU_DX9::DoState(PointerWrap &p) { GPUCommon::DoState(p); diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index 261e21f700..79c287dc47 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -61,7 +61,6 @@ public: primaryInfo = reportingPrimaryInfo_; fullInfo = reportingFullInfo_; } - std::vector GetFramebufferList() override; bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; bool GetCurrentClut(GPUDebugBuffer &buffer) override; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index a36450a494..fc045c3c8c 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -929,10 +929,6 @@ void GPU_GLES::CleanupBeforeUI() { glstate.elementArrayBuffer.bind(0); } -std::vector GPU_GLES::GetFramebufferList() { - return framebufferManagerGL_->GetFramebufferList(); -} - void GPU_GLES::DoState(PointerWrap &p) { GPUCommon::DoState(p); diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index 8919bd0555..deed29b114 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -63,7 +63,6 @@ public: primaryInfo = reportingPrimaryInfo_; fullInfo = reportingFullInfo_; } - std::vector GetFramebufferList() override; bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; bool GetCurrentClut(GPUDebugBuffer &buffer) override; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 36dded6568..0f7bd0cc12 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2506,3 +2506,8 @@ bool GPUCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { bool GPUCommon::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { return textureCache_->GetCurrentTextureDebug(buffer, level); } + +std::vector GPUCommon::GetFramebufferList() { + return framebufferManager_->GetFramebufferList(); +} + diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 56c3068cc2..db77acea0a 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -223,9 +223,7 @@ public: bool DecodeTexture(u8* dest, const GPUgstate &state) override { return false; } - std::vector GetFramebufferList() override { - return std::vector(); - } + std::vector GetFramebufferList(); void ClearShaderCache() override {} void CleanupBeforeUI() override {} diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 57bab8361a..335edcf44e 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -687,25 +687,6 @@ void FramebufferManagerVulkan::DeviceRestore(VulkanContext *vulkan) { InitDeviceObjects(); } -std::vector FramebufferManagerVulkan::GetFramebufferList() { - std::vector list; - - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - - FramebufferInfo info; - info.fb_address = vfb->fb_address; - info.z_address = vfb->z_address; - info.format = vfb->format; - info.width = vfb->width; - info.height = vfb->height; - info.fbo = vfb->fbo; - list.push_back(info); - } - - return list; -} - void FramebufferManagerVulkan::DestroyAllFBOs() { currentRenderVfb_ = 0; displayFramebuf_ = 0; diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index ee78b5cfdc..46b76e49db 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -71,8 +71,6 @@ public: void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst) override; - std::vector GetFramebufferList(); - bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; virtual void RebindFramebuffer() override; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 2e6a8b151d..741eeb485b 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -839,10 +839,6 @@ void GPU_Vulkan::ClearShaderCache() { // TODO } -std::vector GPU_Vulkan::GetFramebufferList() { - return framebufferManagerVulkan_->GetFramebufferList(); -} - void GPU_Vulkan::DoState(PointerWrap &p) { GPUCommon::DoState(p); diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index dfb3d5e81a..1c9998c777 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -84,7 +84,6 @@ public: // Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend. std::vector DebugGetShaderIDs(DebugShaderType shader) override; std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override; - std::vector GetFramebufferList() override; bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; bool DescribeCodePtr(const u8 *ptr, std::string &name) override; From 4a30aedc53f881a02c08cb72c70e5dbfdab64ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 18 Oct 2017 13:03:49 +0200 Subject: [PATCH 034/111] Unify and move around code to cleanup some debug accessors --- GPU/D3D11/FramebufferManagerD3D11.cpp | 10 +-- GPU/D3D11/GPU_D3D11.cpp | 15 ----- GPU/D3D11/GPU_D3D11.h | 4 -- GPU/Directx9/GPU_DX9.cpp | 97 --------------------------- GPU/Directx9/GPU_DX9.h | 4 -- GPU/Directx9/TextureCacheDX9.cpp | 85 +++++++++++++++++++++++ GPU/Directx9/TextureCacheDX9.h | 2 + GPU/GLES/FramebufferManagerGLES.cpp | 19 ------ GPU/GLES/FramebufferManagerGLES.h | 2 - GPU/GLES/GPU_GLES.cpp | 45 ------------- GPU/GLES/GPU_GLES.h | 4 -- GPU/GLES/TextureCacheGLES.cpp | 33 +++++++++ GPU/GLES/TextureCacheGLES.h | 1 + GPU/GPUCommon.cpp | 18 +++-- GPU/GPUCommon.h | 2 + GPU/Vulkan/GPU_Vulkan.cpp | 4 -- GPU/Vulkan/GPU_Vulkan.h | 1 - GPU/Vulkan/TextureCacheVulkan.cpp | 5 ++ GPU/Vulkan/TextureCacheVulkan.h | 2 + 19 files changed, 149 insertions(+), 204 deletions(-) diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 399ac87508..ee1e13e64d 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -275,7 +275,7 @@ void FramebufferManagerD3D11::CompilePostShader() { } void FramebufferManagerD3D11::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - u8 *convBuf = NULL; + u8 *convBuf = nullptr; // TODO: Check / use D3DCAPS2_DYNAMICTEXTURES? if (drawPixelsTex_ && (drawPixelsTexW_ != width || drawPixelsTexH_ != height)) { @@ -719,10 +719,10 @@ void FramebufferManagerD3D11::DeviceLost() { } void FramebufferManagerD3D11::DestroyAllFBOs() { - currentRenderVfb_ = 0; - displayFramebuf_ = 0; - prevDisplayFramebuf_ = 0; - prevPrevDisplayFramebuf_ = 0; + currentRenderVfb_ = nullptr; + displayFramebuf_ = nullptr; + prevDisplayFramebuf_ = nullptr; + prevPrevDisplayFramebuf_ = nullptr; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 1b29b1ce4d..95dadcf07d 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -770,21 +770,6 @@ void GPU_D3D11::DoState(PointerWrap &p) { } } -bool GPU_D3D11::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { - if (!gstate.isTextureMapEnabled()) { - return false; - } - return textureCacheD3D11_->GetCurrentTextureDebug(buffer, level); -} - -bool GPU_D3D11::GetCurrentClut(GPUDebugBuffer &buffer) { - return textureCacheD3D11_->GetCurrentClutBuffer(buffer); -} - -bool GPU_D3D11::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { - return drawEngine_.GetCurrentSimpleVertices(count, vertices, indices); -} - std::vector GPU_D3D11::DebugGetShaderIDs(DebugShaderType type) { switch (type) { case SHADER_TYPE_VERTEXLOADER: diff --git a/GPU/D3D11/GPU_D3D11.h b/GPU/D3D11/GPU_D3D11.h index aa94cd13ee..c83bc35534 100644 --- a/GPU/D3D11/GPU_D3D11.h +++ b/GPU/D3D11/GPU_D3D11.h @@ -61,10 +61,6 @@ public: fullInfo = reportingFullInfo_; } - bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; - bool GetCurrentClut(GPUDebugBuffer &buffer) override; - bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; - typedef void (GPU_D3D11::*CmdFunc)(u32 op, u32 diff); struct CommandInfo { uint64_t flags; diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index ac2f7930a6..3221e487a9 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -703,103 +703,6 @@ void GPU_DX9::DoState(PointerWrap &p) { } } -bool GPU_DX9::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { - if (!gstate.isTextureMapEnabled()) { - return false; - } - - textureCacheDX9_->SetTexture(true); - textureCacheDX9_->ApplyTexture(); - int w = gstate.getTextureWidth(level); - int h = gstate.getTextureHeight(level); - - LPDIRECT3DBASETEXTURE9 baseTex; - LPDIRECT3DTEXTURE9 tex; - LPDIRECT3DSURFACE9 offscreen = nullptr; - HRESULT hr; - - bool success = false; - hr = device_->GetTexture(0, &baseTex); - if (SUCCEEDED(hr) && baseTex != NULL) { - hr = baseTex->QueryInterface(IID_IDirect3DTexture9, (void **)&tex); - if (SUCCEEDED(hr)) { - D3DSURFACE_DESC desc; - D3DLOCKED_RECT locked; - tex->GetLevelDesc(level, &desc); - RECT rect = {0, 0, (LONG)desc.Width, (LONG)desc.Height}; - hr = tex->LockRect(level, &locked, &rect, D3DLOCK_READONLY); - - // If it fails, this means it's a render-to-texture, so we have to get creative. - if (FAILED(hr)) { - LPDIRECT3DSURFACE9 renderTarget = nullptr; - hr = tex->GetSurfaceLevel(level, &renderTarget); - if (renderTarget && SUCCEEDED(hr)) { - hr = device_->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &offscreen, NULL); - if (SUCCEEDED(hr)) { - hr = device_->GetRenderTargetData(renderTarget, offscreen); - if (SUCCEEDED(hr)) { - hr = offscreen->LockRect(&locked, &rect, D3DLOCK_READONLY); - } - } - renderTarget->Release(); - } - } - - if (SUCCEEDED(hr)) { - GPUDebugBufferFormat fmt; - int pixelSize; - switch (desc.Format) { - case D3DFMT_A1R5G5B5: - fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_5551 : GPU_DBG_FORMAT_5551_BGRA; - pixelSize = 2; - break; - case D3DFMT_A4R4G4B4: - fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_4444 : GPU_DBG_FORMAT_4444_BGRA; - pixelSize = 2; - break; - case D3DFMT_R5G6B5: - fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_565 : GPU_DBG_FORMAT_565_BGRA; - pixelSize = 2; - break; - case D3DFMT_A8R8G8B8: - fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_8888 : GPU_DBG_FORMAT_8888_BGRA; - pixelSize = 4; - break; - default: - fmt = GPU_DBG_FORMAT_INVALID; - break; - } - - if (fmt != GPU_DBG_FORMAT_INVALID) { - buffer.Allocate(locked.Pitch / pixelSize, desc.Height, fmt, false); - memcpy(buffer.GetData(), locked.pBits, locked.Pitch * desc.Height); - success = true; - } else { - success = false; - } - if (offscreen) { - offscreen->UnlockRect(); - offscreen->Release(); - } else { - tex->UnlockRect(level); - } - } - tex->Release(); - } - baseTex->Release(); - } - - return success; -} - -bool GPU_DX9::GetCurrentClut(GPUDebugBuffer &buffer) { - return textureCacheDX9_->GetCurrentClutBuffer(buffer); -} - -bool GPU_DX9::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { - return drawEngine_.GetCurrentSimpleVertices(count, vertices, indices); -} - std::vector GPU_DX9::DebugGetShaderIDs(DebugShaderType type) { switch (type) { case SHADER_TYPE_VERTEXLOADER: diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index 79c287dc47..72e76267ff 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -62,10 +62,6 @@ public: fullInfo = reportingFullInfo_; } - bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; - bool GetCurrentClut(GPUDebugBuffer &buffer) override; - bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; - typedef void (GPU_DX9::*CmdFunc)(u32 op, u32 diff); struct CommandInfo { uint64_t flags; diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index f7227a8fd0..5dde89a732 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -785,4 +785,89 @@ bool TextureCacheDX9::DecodeTexture(u8 *output, const GPUgstate &state) return true; } +bool TextureCacheDX9::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { + SetTexture(true); + ApplyTexture(); + int w = gstate.getTextureWidth(level); + int h = gstate.getTextureHeight(level); + + LPDIRECT3DBASETEXTURE9 baseTex; + LPDIRECT3DTEXTURE9 tex; + LPDIRECT3DSURFACE9 offscreen = nullptr; + HRESULT hr; + + bool success = false; + hr = device_->GetTexture(0, &baseTex); + if (SUCCEEDED(hr) && baseTex != NULL) { + hr = baseTex->QueryInterface(IID_IDirect3DTexture9, (void **)&tex); + if (SUCCEEDED(hr)) { + D3DSURFACE_DESC desc; + D3DLOCKED_RECT locked; + tex->GetLevelDesc(level, &desc); + RECT rect = { 0, 0, (LONG)desc.Width, (LONG)desc.Height }; + hr = tex->LockRect(level, &locked, &rect, D3DLOCK_READONLY); + + // If it fails, this means it's a render-to-texture, so we have to get creative. + if (FAILED(hr)) { + LPDIRECT3DSURFACE9 renderTarget = nullptr; + hr = tex->GetSurfaceLevel(level, &renderTarget); + if (renderTarget && SUCCEEDED(hr)) { + hr = device_->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &offscreen, NULL); + if (SUCCEEDED(hr)) { + hr = device_->GetRenderTargetData(renderTarget, offscreen); + if (SUCCEEDED(hr)) { + hr = offscreen->LockRect(&locked, &rect, D3DLOCK_READONLY); + } + } + renderTarget->Release(); + } + } + + if (SUCCEEDED(hr)) { + GPUDebugBufferFormat fmt; + int pixelSize; + switch (desc.Format) { + case D3DFMT_A1R5G5B5: + fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_5551 : GPU_DBG_FORMAT_5551_BGRA; + pixelSize = 2; + break; + case D3DFMT_A4R4G4B4: + fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_4444 : GPU_DBG_FORMAT_4444_BGRA; + pixelSize = 2; + break; + case D3DFMT_R5G6B5: + fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_565 : GPU_DBG_FORMAT_565_BGRA; + pixelSize = 2; + break; + case D3DFMT_A8R8G8B8: + fmt = gstate_c.bgraTexture ? GPU_DBG_FORMAT_8888 : GPU_DBG_FORMAT_8888_BGRA; + pixelSize = 4; + break; + default: + fmt = GPU_DBG_FORMAT_INVALID; + break; + } + + if (fmt != GPU_DBG_FORMAT_INVALID) { + buffer.Allocate(locked.Pitch / pixelSize, desc.Height, fmt, false); + memcpy(buffer.GetData(), locked.pBits, locked.Pitch * desc.Height); + success = true; + } else { + success = false; + } + if (offscreen) { + offscreen->UnlockRect(); + offscreen->Release(); + } else { + tex->UnlockRect(level); + } + } + tex->Release(); + } + baseTex->Release(); + } + + return success; +} + }; diff --git a/GPU/Directx9/TextureCacheDX9.h b/GPU/Directx9/TextureCacheDX9.h index ed8a406852..8e4962bd3a 100644 --- a/GPU/Directx9/TextureCacheDX9.h +++ b/GPU/Directx9/TextureCacheDX9.h @@ -57,6 +57,8 @@ public: void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight); + bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override; + protected: void BindTexture(TexCacheEntry *entry) override; void Unbind() override; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index e622264eca..3eb62e0bcd 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -1027,25 +1027,6 @@ void FramebufferManagerGLES::DeviceLost() { DestroyDraw2DProgram(); } -std::vector FramebufferManagerGLES::GetFramebufferList() { - std::vector list; - - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - - FramebufferInfo info; - info.fb_address = vfb->fb_address; - info.z_address = vfb->z_address; - info.format = vfb->format; - info.width = vfb->width; - info.height = vfb->height; - info.fbo = vfb->fbo; - list.push_back(info); - } - - return list; -} - void FramebufferManagerGLES::DestroyAllFBOs() { CHECK_GL_ERROR_IF_DEBUG(); currentRenderVfb_ = 0; diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 5eb4ccda87..f867f833d6 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -78,8 +78,6 @@ public: void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) override; - std::vector GetFramebufferList(); - bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index fc045c3c8c..a3b0dfd52c 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -946,51 +946,6 @@ void GPU_GLES::DoState(PointerWrap &p) { } } -bool GPU_GLES::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { - if (!gstate.isTextureMapEnabled()) { - return false; - } - -#ifndef USING_GLES2 - GPUgstate saved; - if (level != 0) { - saved = gstate; - - // The way we set textures is a bit complex. Let's just override level 0. - gstate.texsize[0] = gstate.texsize[level]; - gstate.texaddr[0] = gstate.texaddr[level]; - gstate.texbufwidth[0] = gstate.texbufwidth[level]; - } - - textureCacheGL_->SetTexture(true); - textureCacheGL_->ApplyTexture(); - int w = gstate.getTextureWidth(level); - int h = gstate.getTextureHeight(level); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - - if (level != 0) { - gstate = saved; - } - - buffer.Allocate(w, h, GE_FORMAT_8888, false); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); - - return true; -#else - return false; -#endif -} - -bool GPU_GLES::GetCurrentClut(GPUDebugBuffer &buffer) { - return textureCacheGL_->GetCurrentClutBuffer(buffer); -} - -bool GPU_GLES::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { - return drawEngine_.GetCurrentSimpleVertices(count, vertices, indices); -} - bool GPU_GLES::DescribeCodePtr(const u8 *ptr, std::string &name) { if (drawEngine_.IsCodePtrVertexDecoder(ptr)) { name = "VertexDecoderJit"; diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index deed29b114..fd7bff792e 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -64,10 +64,6 @@ public: fullInfo = reportingFullInfo_; } - bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; - bool GetCurrentClut(GPUDebugBuffer &buffer) override; - bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; - bool DescribeCodePtr(const u8 *ptr, std::string &name) override; typedef void (GPU_GLES::*CmdFunc)(u32 op, u32 diff); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 044425da19..ca70e0a052 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -1025,3 +1025,36 @@ bool TextureCacheGLES::DecodeTexture(u8* output, const GPUgstate &state) { gstate = oldState; return true; } + +bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { +#ifndef USING_GLES2 + GPUgstate saved; + if (level != 0) { + saved = gstate; + + // The way we set textures is a bit complex. Let's just override level 0. + gstate.texsize[0] = gstate.texsize[level]; + gstate.texaddr[0] = gstate.texaddr[level]; + gstate.texbufwidth[0] = gstate.texbufwidth[level]; + } + + SetTexture(true); + ApplyTexture(); + int w = gstate.getTextureWidth(level); + int h = gstate.getTextureHeight(level); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); + + if (level != 0) { + gstate = saved; + } + + buffer.Allocate(w, h, GE_FORMAT_8888, false); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); + + return true; +#else + return false; +#endif +} diff --git a/GPU/GLES/TextureCacheGLES.h b/GPU/GLES/TextureCacheGLES.h index b7c1eeaeb3..e00a43afd6 100644 --- a/GPU/GLES/TextureCacheGLES.h +++ b/GPU/GLES/TextureCacheGLES.h @@ -67,6 +67,7 @@ public: bool DecodeTexture(u8 *output, const GPUgstate &state); void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight); + bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override; protected: void BindTexture(TexCacheEntry *entry) override; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 0f7bd0cc12..1debe2840c 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2503,11 +2503,21 @@ bool GPUCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { return framebufferManager_ ? framebufferManager_->GetOutputFramebuffer(buffer) : false; } -bool GPUCommon::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { - return textureCache_->GetCurrentTextureDebug(buffer, level); -} - std::vector GPUCommon::GetFramebufferList() { return framebufferManager_->GetFramebufferList(); } +bool GPUCommon::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { + return drawEngineCommon_->GetCurrentSimpleVertices(count, vertices, indices); +} + +bool GPUCommon::GetCurrentClut(GPUDebugBuffer &buffer) { + return textureCache_->GetCurrentClutBuffer(buffer); +} + +bool GPUCommon::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { + if (!gstate.isTextureMapEnabled()) { + return false; + } + return textureCache_->GetCurrentTextureDebug(buffer, level); +} diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index db77acea0a..80455fe20c 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -179,6 +179,8 @@ public: bool GetCurrentDepthbuffer(GPUDebugBuffer &buffer) override; bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer) override; bool GetCurrentTexture(GPUDebugBuffer &buffer, int level) override; + bool GetCurrentClut(GPUDebugBuffer &buffer) override; + bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; std::vector ActiveDisplayLists() override; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 741eeb485b..d95a83abfe 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -856,10 +856,6 @@ void GPU_Vulkan::DoState(PointerWrap &p) { } } -bool GPU_Vulkan::GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) { - return drawEngine_.GetCurrentSimpleVertices(count, vertices, indices); -} - bool GPU_Vulkan::DescribeCodePtr(const u8 *ptr, std::string &name) { if (drawEngine_.IsCodePtrVertexDecoder(ptr)) { name = "VertexDecoderJit"; diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index 1c9998c777..5131f3fad6 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -84,7 +84,6 @@ public: // Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend. std::vector DebugGetShaderIDs(DebugShaderType shader) override; std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override; - bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; bool DescribeCodePtr(const u8 *ptr, std::string &name) override; protected: diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 11e930399d..9cea56729b 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -778,3 +778,8 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt } } } + +bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { + // TODO + return false; +} diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 809606aa00..21beb9a18d 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -100,6 +100,8 @@ public: } void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, SamplerCacheKey &key); + bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override; + protected: void BindTexture(TexCacheEntry *entry) override; void Unbind() override; From b886efe8f586a4cf1223358c084aed1dbdc3401d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 20 Oct 2017 11:06:06 +0200 Subject: [PATCH 035/111] Another minor cleanup (DescribeCodePtr) --- GPU/Common/DrawEngineCommon.h | 4 ++++ GPU/D3D11/DrawEngineD3D11.cpp | 4 ---- GPU/D3D11/DrawEngineD3D11.h | 2 -- GPU/Directx9/DrawEngineDX9.cpp | 4 ---- GPU/Directx9/DrawEngineDX9.h | 2 -- GPU/GLES/GPU_GLES.cpp | 8 -------- GPU/GLES/GPU_GLES.h | 2 -- GPU/GPUCommon.cpp | 8 ++++++++ GPU/GPUCommon.h | 15 ++++++--------- GPU/Vulkan/DrawEngineVulkan.cpp | 4 ---- GPU/Vulkan/DrawEngineVulkan.h | 2 -- GPU/Vulkan/GPU_Vulkan.cpp | 8 -------- GPU/Vulkan/GPU_Vulkan.h | 1 - 13 files changed, 18 insertions(+), 46 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 20b581def7..99023d63f1 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -71,6 +71,10 @@ public: void SetupVertexDecoder(u32 vertType); + bool IsCodePtrVertexDecoder(const u8 *ptr) const { + return decJitCache_->IsInSpace(ptr); + } + protected: virtual void ClearTrackedVertexArrays() {} diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index ecf1ad3206..e8e6ba8580 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -786,10 +786,6 @@ rotateVBO: #endif } -bool DrawEngineD3D11::IsCodePtrVertexDecoder(const u8 *ptr) const { - return decJitCache_->IsInSpace(ptr); -} - void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) { // Position if (prevSize < size) { diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index 3a7d347932..7175a78e61 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -136,8 +136,6 @@ public: DecodeVerts(); } - bool IsCodePtrVertexDecoder(const u8 *ptr) const; - void DispatchFlush() override { Flush(); } void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override { SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead); diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 1cd8d66f2e..68d5d837fb 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -707,10 +707,6 @@ rotateVBO: host->GPUNotifyDraw(); } -bool DrawEngineDX9::IsCodePtrVertexDecoder(const u8 *ptr) const { - return decJitCache_->IsInSpace(ptr); -} - void DrawEngineDX9::TessellationDataTransferDX9::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) { } diff --git a/GPU/Directx9/DrawEngineDX9.h b/GPU/Directx9/DrawEngineDX9.h index 4c4f96acc3..58bbfb3888 100644 --- a/GPU/Directx9/DrawEngineDX9.h +++ b/GPU/Directx9/DrawEngineDX9.h @@ -133,8 +133,6 @@ public: DecodeVerts(); } - bool IsCodePtrVertexDecoder(const u8 *ptr) const; - void DispatchFlush() override { Flush(); } void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override { SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead); diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index a3b0dfd52c..b7d25debd3 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -946,14 +946,6 @@ void GPU_GLES::DoState(PointerWrap &p) { } } -bool GPU_GLES::DescribeCodePtr(const u8 *ptr, std::string &name) { - if (drawEngine_.IsCodePtrVertexDecoder(ptr)) { - name = "VertexDecoderJit"; - return true; - } - return false; -} - std::vector GPU_GLES::DebugGetShaderIDs(DebugShaderType type) { switch (type) { case SHADER_TYPE_VERTEXLOADER: diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index fd7bff792e..e15a26c4d8 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -64,8 +64,6 @@ public: fullInfo = reportingFullInfo_; } - bool DescribeCodePtr(const u8 *ptr, std::string &name) override; - typedef void (GPU_GLES::*CmdFunc)(u32 op, u32 diff); struct CommandInfo { uint64_t flags; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 1debe2840c..53b39e6d32 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2521,3 +2521,11 @@ bool GPUCommon::GetCurrentTexture(GPUDebugBuffer &buffer, int level) { } return textureCache_->GetCurrentTextureDebug(buffer, level); } + +bool GPUCommon::DescribeCodePtr(const u8 *ptr, std::string &name) { + if (drawEngineCommon_->IsCodePtrVertexDecoder(ptr)) { + name = "VertexDecoderJit"; + return true; + } + return false; +} diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 80455fe20c..0eceaa0b4c 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -169,10 +169,6 @@ public: #define new DBG_NEW #endif - bool DescribeCodePtr(const u8 *ptr, std::string &name) override { - return false; - } - // From GPUDebugInterface. bool GetCurrentDisplayList(DisplayList &list) override; bool GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes) override; @@ -183,6 +179,12 @@ public: bool GetCurrentSimpleVertices(int count, std::vector &vertices, std::vector &indices) override; bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override; + std::vector DebugGetShaderIDs(DebugShaderType shader) override { return std::vector(); }; + std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override { + return "N/A"; + } + bool DescribeCodePtr(const u8 *ptr, std::string &name); + std::vector ActiveDisplayLists() override; void ResetListPC(int listID, u32 pc) override; void ResetListStall(int listID, u32 stall) override; @@ -236,11 +238,6 @@ public: return -1; } - std::vector DebugGetShaderIDs(DebugShaderType shader) override { return std::vector(); }; - std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override { - return "N/A"; - } - typedef void (GPUCommon::*CmdFunc)(u32 op, u32 diff); protected: diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 0d9a0093a6..07e46aee86 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -1078,10 +1078,6 @@ void DrawEngineVulkan::UpdateUBOs(FrameData *frame) { } } -bool DrawEngineVulkan::IsCodePtrVertexDecoder(const u8 *ptr) const { - return decJitCache_->IsInSpace(ptr); -} - void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) { int rowPitch; diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index aa40ebf98d..1bb2d3295c 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -147,8 +147,6 @@ public: DoFlush(); } - bool IsCodePtrVertexDecoder(const u8 *ptr) const; - void DispatchFlush() override { Flush(); } void DispatchSubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) override { SubmitPrim(verts, inds, prim, vertexCount, vertType, bytesRead); diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index d95a83abfe..f1fb20e9bc 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -856,14 +856,6 @@ void GPU_Vulkan::DoState(PointerWrap &p) { } } -bool GPU_Vulkan::DescribeCodePtr(const u8 *ptr, std::string &name) { - if (drawEngine_.IsCodePtrVertexDecoder(ptr)) { - name = "VertexDecoderJit"; - return true; - } - return false; -} - std::vector GPU_Vulkan::DebugGetShaderIDs(DebugShaderType type) { if (type == SHADER_TYPE_VERTEXLOADER) { return drawEngine_.DebugGetVertexLoaderIDs(); diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index 5131f3fad6..1e7dc06ec2 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -84,7 +84,6 @@ public: // Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend. std::vector DebugGetShaderIDs(DebugShaderType shader) override; std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override; - bool DescribeCodePtr(const u8 *ptr, std::string &name) override; protected: void FastRunLoop(DisplayList &list) override; From 21d3c54401982d87065ab23a4bbaccd164292538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 20 Oct 2017 11:11:04 +0200 Subject: [PATCH 036/111] Have GetFramebufferDimensions return the backbuffer size for null in all backends. --- ext/native/thin3d/thin3d_d3d9.cpp | 9 +++++++-- ext/native/thin3d/thin3d_vulkan.cpp | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index eef76d9ffa..6665fa263d 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -1118,8 +1118,13 @@ void D3D9Context::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChan void D3D9Context::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { D3D9Framebuffer *fb = (D3D9Framebuffer *)fbo; - *w = fb->width; - *h = fb->height; + if (fb) { + *w = fb->width; + *h = fb->height; + } else { + *w = targetWidth_; + *h = targetHeight_; + } } bool D3D9Context::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 7522060d09..bf5fa86959 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -2029,8 +2029,13 @@ uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { VKFramebuffer *fb = (VKFramebuffer *)fbo; - *w = fb->width; - *h = fb->height; + if (fb) { + *w = fb->width; + *h = fb->height; + } else { + *w = vulkan_->GetWidth(); + *h = vulkan_->GetHeight(); + } } void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void *param2) { From 017d3da067f526360820c52a10fb1bde87d7ab5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 20 Oct 2017 11:53:07 +0200 Subject: [PATCH 037/111] Allow D3D9 context creation to bail if shader compilation fails (D3D9 runtime not installed). Fixes an issue where after switching to D3D9 on a PC without the D3D9 runtime, it's impossible to start PPSSPP anymore. --- UWP/PPSSPP_UWPMain.cpp | 2 ++ Windows/GPU/D3D11Context.cpp | 2 ++ Windows/GPU/D3D9Context.cpp | 7 +++++++ Windows/GPU/WindowsGLContext.cpp | 4 ++-- Windows/GPU/WindowsVulkanContext.cpp | 2 +- android/jni/app-android.cpp | 3 +++ ext/native/base/PCMain.cpp | 2 ++ ext/native/base/QtMain.h | 3 +++ ext/native/thin3d/thin3d.cpp | 4 +++- ext/native/thin3d/thin3d.h | 3 +-- ext/native/thin3d/thin3d_d3d11.cpp | 1 - ext/native/thin3d/thin3d_d3d9.cpp | 23 +++++++++++++++-------- ext/native/thin3d/thin3d_gl.cpp | 2 -- ext/native/thin3d/thin3d_vulkan.cpp | 1 - headless/SDLHeadlessHost.cpp | 2 ++ ios/ViewController.mm | 2 ++ 16 files changed, 45 insertions(+), 18 deletions(-) diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 7f764c03be..50941a9f45 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "PPSSPP_UWPMain.h" +#include #include #include "base/basictypes.h" @@ -323,6 +324,7 @@ void PPSSPP_UWPMain::LoadStorageFile(StorageFile ^file) { UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr resources) { draw_ = Draw::T3DCreateD3D11Context( resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetDeviceFeatureLevel(), 0); + assert(draw_->CreatePresets()); } void UWPGraphicsContext::Shutdown() { diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index bed1ff2f8d..0917faa2f0 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -2,6 +2,7 @@ #include "Common/CommonWindows.h" #include +#include #include "base/logging.h" #include "util/text/utf8.h" @@ -139,6 +140,7 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { #endif draw_ = Draw::T3DCreateD3D11Context(device_, context_, device1_, context1_, featureLevel_, hWnd_); + assert(draw_->CreatePresets()); // If we can run D3D11, there's a compiler installed. I think. int width; int height; diff --git a/Windows/GPU/D3D9Context.cpp b/Windows/GPU/D3D9Context.cpp index 096251677f..ec7ffdb0ca 100644 --- a/Windows/GPU/D3D9Context.cpp +++ b/Windows/GPU/D3D9Context.cpp @@ -165,6 +165,13 @@ bool D3D9Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { //deviceEx->SetMaximumFrameLatency(1); } draw_ = Draw::T3DCreateDX9Context(d3d_, d3dEx_, adapterId_, device_, deviceEx_); + if (!draw_->CreatePresets()) { + // Shader compiler not installed? Return an error so we can fall back to GL. + device_->Release(); + d3d_->Release(); + *error_message = "DirectX9 runtime not correctly installed. Please install."; + return false; + } if (draw_) draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, 0, 0, nullptr); return true; diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index d1b6b73563..febdcc799f 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -15,8 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -// TODO: What a mess this is :( - +#include #include "Common/Log.h" #include "Common/CommonWindows.h" #include "gfx/gl_common.h" @@ -358,6 +357,7 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + assert(draw_->CreatePresets()); // if we get this far, there will always be a GLSL compiler capable of compiling these. CHECK_GL_ERROR_IF_DEBUG(); return true; // Success } diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 0f4d18118f..a4cda9b9c0 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -203,7 +203,7 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m } draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); - + assert(draw_->CreatePresets()); // Doesn't fail, we include the compiler. return true; } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index c04d46c309..2c18f4fef4 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -133,6 +133,7 @@ bool AndroidEGLGraphicsContext::Init(ANativeWindow *wnd, int backbufferWidth, in gl->MakeCurrent(); CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + assert(draw_->CreatePresets()); // There will always be a GLSL compiler capable of compiling these. return true; } @@ -157,6 +158,7 @@ public: AndroidJavaEGLGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + assert(draw_->CreatePresets()); } ~AndroidJavaEGLGraphicsContext() { delete draw_; @@ -310,6 +312,7 @@ bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, } g_Vulkan->InitObjects(true); draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); + assert(draw_->CreatePresets()); // Doesn't fail, we ship the compiler. return true; } diff --git a/ext/native/base/PCMain.cpp b/ext/native/base/PCMain.cpp index dc734a5048..64c6a7c547 100644 --- a/ext/native/base/PCMain.cpp +++ b/ext/native/base/PCMain.cpp @@ -27,6 +27,7 @@ SDLJoystick *joystick = NULL; #endif #include +#include #include "base/display.h" #include "base/logging.h" @@ -51,6 +52,7 @@ public: GLDummyGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + assert(draw_->CreatePresets()); } ~GLDummyGraphicsContext() { delete draw_; } diff --git a/ext/native/base/QtMain.h b/ext/native/base/QtMain.h index 0fda2cde4a..a8e947be03 100644 --- a/ext/native/base/QtMain.h +++ b/ext/native/base/QtMain.h @@ -18,6 +18,8 @@ QTM_USE_NAMESPACE #endif #endif +#include + #include "base/display.h" #include "base/logging.h" #include "base/timeutil.h" @@ -42,6 +44,7 @@ class QtDummyGraphicsContext : public DummyGraphicsContext { public: QtDummyGraphicsContext() { draw_ = Draw::T3DCreateGLContext(); + assert(draw_->CreatePresets()); } ~QtDummyGraphicsContext() { delete draw_; diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 44dc740664..1de3bbba4d 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -299,12 +299,14 @@ static ShaderModule *CreateShader(DrawContext *draw, ShaderStage stage, const st return nullptr; } -void DrawContext::CreatePresets() { +bool DrawContext::CreatePresets() { vsPresets_[VS_TEXTURE_COLOR_2D] = CreateShader(this, ShaderStage::VERTEX, vsTexCol); vsPresets_[VS_COLOR_2D] = CreateShader(this, ShaderStage::VERTEX, vsCol); fsPresets_[FS_TEXTURE_COLOR_2D] = CreateShader(this, ShaderStage::FRAGMENT, fsTexCol); fsPresets_[FS_COLOR_2D] = CreateShader(this, ShaderStage::FRAGMENT, fsCol); + + return vsPresets_[VS_TEXTURE_COLOR_2D] && vsPresets_[VS_COLOR_2D] && fsPresets_[FS_TEXTURE_COLOR_2D] && fsPresets_[FS_COLOR_2D]; } DrawContext::~DrawContext() { diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 3c8dcc73e0..78c8ea6b2b 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -583,6 +583,7 @@ struct RenderPassInfo { class DrawContext { public: virtual ~DrawContext(); + bool CreatePresets(); virtual const DeviceCaps &GetDeviceCaps() const = 0; virtual uint32_t GetDataFormatSupport(DataFormat fmt) const = 0; @@ -691,8 +692,6 @@ public: virtual void FlushState() {} protected: - void CreatePresets(); - ShaderModule *vsPresets_[VS_MAX_PRESET]; ShaderModule *fsPresets_[FS_MAX_PRESET]; diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 45db38b91a..554bdca80b 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -242,7 +242,6 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de } dxgiDevice->Release(); } - CreatePresets(); // Temp texture for read-back of small images. Custom textures are created on demand for larger ones. // TODO: Should really benchmark if this extra complexity has any benefit. diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 6665fa263d..ad40c89a44 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -585,7 +585,6 @@ private: D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, IDirect3DDevice9 *device, IDirect3DDevice9Ex *deviceEx) : d3d_(d3d), d3dEx_(d3dEx), adapterId_(adapterId), device_(device), deviceEx_(deviceEx), caps_{} { - CreatePresets(); if (FAILED(d3d->GetAdapterIdentifier(adapterId, 0, &identifier_))) { ELOG("Failed to get adapter identifier: %d", adapterId); } @@ -633,6 +632,10 @@ Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) { } D3D9Pipeline *pipeline = new D3D9Pipeline(device_); for (auto iter : desc.shaders) { + if (!iter) { + ELOG("NULL shader passed to CreateGraphicsPipeline"); + return false; + } if (iter->GetStage() == ShaderStage::FRAGMENT) { pipeline->pshader = static_cast(iter); } @@ -947,20 +950,24 @@ void D3D9Context::SetBlendFactor(float color[4]) { } bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, size_t size) { - LPD3DXMACRO defines = NULL; - LPD3DXINCLUDE includes = NULL; + LPD3DXMACRO defines = nullptr; + LPD3DXINCLUDE includes = nullptr; DWORD flags = 0; - LPD3DXBUFFER codeBuffer; - LPD3DXBUFFER errorBuffer; + LPD3DXBUFFER codeBuffer = nullptr; + LPD3DXBUFFER errorBuffer = nullptr; const char *source = (const char *)data; const char *profile = stage_ == ShaderStage::FRAGMENT ? "ps_2_0" : "vs_2_0"; HRESULT hr = dyn_D3DXCompileShader(source, (UINT)strlen(source), defines, includes, "main", profile, flags, &codeBuffer, &errorBuffer, nullptr); if (FAILED(hr)) { - const char *error = (const char *)errorBuffer->GetBufferPointer(); + const char *error = errorBuffer ? (const char *)errorBuffer->GetBufferPointer() : "(no errorbuffer returned)"; + if (hr == ERROR_MOD_NOT_FOUND) { + // No D3D9-compatible shader compiler installed. + error = "D3D9 shader compiler not installed"; + } OutputDebugStringA(source); OutputDebugStringA(error); - errorBuffer->Release(); - + if (errorBuffer) + errorBuffer->Release(); if (codeBuffer) codeBuffer->Release(); return false; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index b8dfbb025d..65dcdd4a33 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -591,8 +591,6 @@ private: }; OpenGLContext::OpenGLContext() { - CreatePresets(); - // TODO: Detect more caps if (gl_extensions.IsGLES) { if (gl_extensions.OES_packed_depth_stencil || gl_extensions.OES_depth24) { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index bf5fa86959..d559bc84c1 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -719,7 +719,6 @@ VKContext::VKContext(VulkanContext *vulkan) viewport_.minDepth = 0.0f; viewport_.maxDepth = 0.0f; memset(boundTextures_, 0, sizeof(boundTextures_)); - CreatePresets(); VkDescriptorPoolSize dpTypes[2]; dpTypes[0].descriptorCount = 200; diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index 8e0e1ab082..5b96f09953 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "gfx/gl_lost_manager.h" #include "headless/SDLHeadlessHost.h" @@ -54,6 +55,7 @@ public: GLDummyGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + assert(draw_->CreatePresets()); } ~GLDummyGraphicsContext() { delete draw_; } diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 1cb6e5230f..02a3b343e1 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -8,6 +8,7 @@ #import "ViewController.h" #import "AudioEngine.h" #import +#include #include "base/display.h" #include "base/timeutil.h" @@ -38,6 +39,7 @@ public: IOSDummyGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); + assert(draw_->CreatePresets()); } ~IOSDummyGraphicsContext() { delete draw_; From b9ba525de115be776412c99bf02dc9a6f71658df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 20 Oct 2017 14:45:00 +0200 Subject: [PATCH 038/111] Don't call functions in asserts, they get compiled out in release... --- UWP/PPSSPP_UWPMain.cpp | 3 ++- Windows/GPU/D3D11Context.cpp | 3 ++- Windows/GPU/WindowsGLContext.cpp | 3 ++- Windows/GPU/WindowsVulkanContext.cpp | 5 +++-- android/jni/app-android.cpp | 9 ++++++--- ext/native/base/PCMain.cpp | 3 ++- ext/native/base/QtMain.h | 3 ++- headless/SDLHeadlessHost.cpp | 3 ++- ios/ViewController.mm | 3 ++- 9 files changed, 23 insertions(+), 12 deletions(-) diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 50941a9f45..96d346b819 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -324,7 +324,8 @@ void PPSSPP_UWPMain::LoadStorageFile(StorageFile ^file) { UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr resources) { draw_ = Draw::T3DCreateD3D11Context( resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetDeviceFeatureLevel(), 0); - assert(draw_->CreatePresets()); + bool success = draw_->CreatePresets(); + assert(success); } void UWPGraphicsContext::Shutdown() { diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index 0917faa2f0..bf7765c771 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -140,7 +140,8 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { #endif draw_ = Draw::T3DCreateD3D11Context(device_, context_, device1_, context1_, featureLevel_, hWnd_); - assert(draw_->CreatePresets()); // If we can run D3D11, there's a compiler installed. I think. + bool success = draw_->CreatePresets(); // If we can run D3D11, there's a compiler installed. I think. + assert(success); int width; int height; diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index febdcc799f..c73c0cd298 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -357,7 +357,8 @@ bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_mes CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); - assert(draw_->CreatePresets()); // if we get this far, there will always be a GLSL compiler capable of compiling these. + bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these. + assert(success); CHECK_GL_ERROR_IF_DEBUG(); return true; // Success } diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index a4cda9b9c0..6565ceee6e 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -203,8 +203,9 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m } draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); - assert(draw_->CreatePresets()); // Doesn't fail, we include the compiler. - return true; + bool success = draw_->CreatePresets(); + assert(success); // Doesn't fail, we include the compiler. + return success; } void WindowsVulkanContext::Shutdown() { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 2c18f4fef4..5ed28dbd46 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -133,7 +133,8 @@ bool AndroidEGLGraphicsContext::Init(ANativeWindow *wnd, int backbufferWidth, in gl->MakeCurrent(); CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); - assert(draw_->CreatePresets()); // There will always be a GLSL compiler capable of compiling these. + bool success = draw_->CreatePresets(); // There will always be a GLSL compiler capable of compiling these. + assert(success); return true; } @@ -158,7 +159,8 @@ public: AndroidJavaEGLGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); - assert(draw_->CreatePresets()); + bool success = draw_->CreatePresets(); + assert(success); } ~AndroidJavaEGLGraphicsContext() { delete draw_; @@ -312,7 +314,8 @@ bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, } g_Vulkan->InitObjects(true); draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); - assert(draw_->CreatePresets()); // Doesn't fail, we ship the compiler. + bool success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. + assert(success); return true; } diff --git a/ext/native/base/PCMain.cpp b/ext/native/base/PCMain.cpp index 64c6a7c547..ad4244bf1d 100644 --- a/ext/native/base/PCMain.cpp +++ b/ext/native/base/PCMain.cpp @@ -52,7 +52,8 @@ public: GLDummyGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); - assert(draw_->CreatePresets()); + bool success = draw_->CreatePresets(); + assert(success); } ~GLDummyGraphicsContext() { delete draw_; } diff --git a/ext/native/base/QtMain.h b/ext/native/base/QtMain.h index a8e947be03..d1bcc87fa9 100644 --- a/ext/native/base/QtMain.h +++ b/ext/native/base/QtMain.h @@ -44,7 +44,8 @@ class QtDummyGraphicsContext : public DummyGraphicsContext { public: QtDummyGraphicsContext() { draw_ = Draw::T3DCreateGLContext(); - assert(draw_->CreatePresets()); + bool success = draw_->CreatePresets(); + assert(success); } ~QtDummyGraphicsContext() { delete draw_; diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index 5b96f09953..90c05dbc74 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -55,7 +55,8 @@ public: GLDummyGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); - assert(draw_->CreatePresets()); + bool success = draw_->CreatePresets(); + assert(success); } ~GLDummyGraphicsContext() { delete draw_; } diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 02a3b343e1..4c81509ec5 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -39,7 +39,8 @@ public: IOSDummyGraphicsContext() { CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); - assert(draw_->CreatePresets()); + bool success = draw_->CreatePresets(); + assert(success); } ~IOSDummyGraphicsContext() { delete draw_; From d0b9e5ed8f0748f89865d6e6cb0e82b7fb5bed16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 20 Oct 2017 14:47:36 +0200 Subject: [PATCH 039/111] Initial work on passing the latest Vulkan validation checks. --- Common/Vulkan/VulkanContext.cpp | 25 ++++++++++++++++++---- Common/Vulkan/VulkanContext.h | 6 ++++++ Common/Vulkan/VulkanImage.cpp | 33 ++++++++++++++++++++--------- ext/native/thin3d/thin3d_vulkan.cpp | 4 ++++ 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 248ec0b643..e41dde9a0d 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -185,7 +185,7 @@ void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) { prePresentBarrier.subresourceRange.baseArrayLayer = 0; prePresentBarrier.subresourceRange.layerCount = 1; prePresentBarrier.image = image; - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier); } @@ -739,10 +739,11 @@ void VulkanContext::InitDepthStencilBuffer(VkCommandBuffer cmd) { res = vkBindImageMemory(device_, depth.image, depth.mem, 0); assert(res == VK_SUCCESS); - TransitionImageLayout(cmd, depth.image, + TransitionImageLayout2(cmd, depth.image, aspectMask, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; depth_view_info.image = depth.image; @@ -1283,6 +1284,22 @@ void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlag vkCmdPipelineBarrier(cmd, src_stages, dest_stages, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } +void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, + VkImageLayout oldImageLayout, VkImageLayout newImageLayout, + VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, + VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) { + VkImageMemoryBarrier image_memory_barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + image_memory_barrier.srcAccessMask = srcAccessMask; + image_memory_barrier.dstAccessMask = dstAccessMask; + image_memory_barrier.oldLayout = oldImageLayout; + image_memory_barrier.newLayout = newImageLayout; + image_memory_barrier.image = image; + image_memory_barrier.subresourceRange.aspectMask = aspectMask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = 1; + image_memory_barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); +} EShLanguage FindLanguage(const VkShaderStageFlagBits shader_type) { switch (shader_type) { diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index d9ee98ba9b..40391a254c 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -447,6 +447,12 @@ void TransitionImageLayout( VkImageLayout old_image_layout, VkImageLayout new_image_layout); +// Detailed control. +void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, + VkImageLayout oldImageLayout, VkImageLayout newImageLayout, + VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, + VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); + // GLSL compiler void init_glslang(); void finalize_glslang(); diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp index 7fca19628d..54dc39bba3 100644 --- a/Common/Vulkan/VulkanImage.cpp +++ b/Common/Vulkan/VulkanImage.cpp @@ -145,15 +145,17 @@ void VulkanTexture::Unlock() { assert(res == VK_SUCCESS); // Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL - TransitionImageLayout(cmd, mappableImage, + TransitionImageLayout2(cmd, mappableImage, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PREINITIALIZED, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); - TransitionImageLayout(cmd, image, + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, VK_ACCESS_TRANSFER_WRITE_BIT); VkImageCopy copy_region; copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -183,10 +185,11 @@ void VulkanTexture::Unlock() { assert(res == VK_SUCCESS); // Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY - TransitionImageLayout(cmd, image, + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); // Then drop the temporary mappable image - although should not be necessary... vulkan_->Delete().QueueDeleteImage(mappableImage); @@ -276,7 +279,17 @@ bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI // Write a command to transition the image to the requested layout, if it's not already that layout. if (initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && initialLayout != VK_IMAGE_LAYOUT_PREINITIALIZED) { - TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, initialLayout); + switch (initialLayout) { + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, VK_ACCESS_TRANSFER_WRITE_BIT); + break; + default: + assert(false); + break; + } } vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index d559bc84c1..1ade5640a7 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1012,6 +1012,10 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { int i = 0; for (auto &iter : desc.shaders) { VKShaderModule *vkshader = (VKShaderModule *)iter; + if (!vkshader) { + ELOG("CreateGraphicsPipeline got passed a null shader"); + return nullptr; + } VkPipelineShaderStageCreateInfo &stage = stages[i++]; stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage.pNext = nullptr; From 5da165fad6fb1558c73800b7c25fbabb818462b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 20 Oct 2017 17:19:07 +0200 Subject: [PATCH 040/111] More work towards passing 1.61 Vulkan validation layers --- Common/Vulkan/VulkanContext.cpp | 67 ++--------------------------- Common/Vulkan/VulkanContext.h | 6 --- Common/Vulkan/VulkanImage.cpp | 34 +++++---------- Common/Vulkan/VulkanImage.h | 3 +- GPU/Vulkan/StateMappingVulkan.cpp | 1 - ext/native/thin3d/thin3d_vulkan.cpp | 31 ++++++------- 6 files changed, 32 insertions(+), 110 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index e41dde9a0d..daa21b05fc 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -1047,10 +1047,11 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { // TODO: Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring // in image to render to will be to transition them away from that. - TransitionImageLayout(cmd, sc_buffer.image, + TransitionImageLayout2(cmd, sc_buffer.image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); color_image_view.image = sc_buffer.image; @@ -1224,66 +1225,6 @@ bool VulkanContext::CreateShaderModule(const std::vector &spirv, VkSha } } -void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, VkImageLayout new_image_layout) { - VkImageMemoryBarrier image_memory_barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = 0; - image_memory_barrier.oldLayout = old_image_layout; - image_memory_barrier.newLayout = new_image_layout; - image_memory_barrier.image = image; - image_memory_barrier.subresourceRange.aspectMask = aspectMask; - image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.layerCount = 1; - if (old_image_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { - image_memory_barrier.srcAccessMask |= VK_ACCESS_MEMORY_READ_BIT; - } - if (old_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - image_memory_barrier.srcAccessMask |= VK_ACCESS_SHADER_READ_BIT; - } - - if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - image_memory_barrier.srcAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - if (old_image_layout == VK_IMAGE_LAYOUT_PREINITIALIZED) { - image_memory_barrier.srcAccessMask |= VK_ACCESS_HOST_WRITE_BIT; - } - image_memory_barrier.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - /* Make sure anything that was copying from this image has completed */ - image_memory_barrier.dstAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - /* Make sure any Copy or CPU writes to image are flushed */ - if (old_image_layout != VK_IMAGE_LAYOUT_UNDEFINED) { - image_memory_barrier.srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT; - } - image_memory_barrier.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - image_memory_barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - image_memory_barrier.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - } - - if (new_image_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { - image_memory_barrier.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT; - } - - VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - - vkCmdPipelineBarrier(cmd, src_stages, dest_stages, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); -} - void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 40391a254c..925240b5c7 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -440,12 +440,6 @@ private: // Stand-alone utility functions void VulkanBeginCommandBuffer(VkCommandBuffer cmd); -void TransitionImageLayout( - VkCommandBuffer cmd, - VkImage image, - VkImageAspectFlags aspectMask, - VkImageLayout old_image_layout, - VkImageLayout new_image_layout); // Detailed control. void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp index 54dc39bba3..19291746ae 100644 --- a/Common/Vulkan/VulkanImage.cpp +++ b/Common/Vulkan/VulkanImage.cpp @@ -8,13 +8,6 @@ VkResult VulkanTexture::Create(int w, int h, VkFormat format) { VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(vulkan_->GetPhysicalDevice(), format, &formatProps); - - // See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data. - // Linear tiling is usually only supported for 2D non-array textures. - // needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false; - // Always stage. - needStaging = true; - return VK_SUCCESS; } @@ -39,7 +32,7 @@ void VulkanTexture::CreateMappableImage() { image_create_info.arrayLayers = 1; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; image_create_info.tiling = VK_IMAGE_TILING_LINEAR; - image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; image_create_info.queueFamilyIndexCount = 0; image_create_info.pQueueFamilyIndices = NULL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -98,15 +91,8 @@ void VulkanTexture::Unlock() { // if we already have an image, queue it for destruction and forget it. Wipe(); - if (!needStaging) { - // If we can use the linear tiled image as a texture, just do it - image = mappableImage; - mem = mappableMemory; - TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - // Make sure we don't accidentally delete the main image. - mappableImage = VK_NULL_HANDLE; - mappableMemory = VK_NULL_HANDLE; - } else { + + { // Shrink the diff by not unindenting. If you make major changes, remove this. VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = format_; @@ -369,18 +355,20 @@ void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buf void VulkanTexture::EndCreate() { VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - TransitionImageLayout(cmd, image, + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); } void VulkanTexture::TransitionForUpload() { VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - TransitionImageLayout(cmd, image, + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); } void VulkanTexture::Destroy() { diff --git a/Common/Vulkan/VulkanImage.h b/Common/Vulkan/VulkanImage.h index 3b67119946..50b9b0b651 100644 --- a/Common/Vulkan/VulkanImage.h +++ b/Common/Vulkan/VulkanImage.h @@ -11,7 +11,7 @@ public: VulkanTexture(VulkanContext *vulkan, VulkanDeviceAllocator *allocator = nullptr) : vulkan_(vulkan), image(VK_NULL_HANDLE), mem(VK_NULL_HANDLE), view(VK_NULL_HANDLE), tex_width(0), tex_height(0), numMips_(1), format_(VK_FORMAT_UNDEFINED), - mappableImage(VK_NULL_HANDLE), mappableMemory(VK_NULL_HANDLE), needStaging(false), + mappableImage(VK_NULL_HANDLE), mappableMemory(VK_NULL_HANDLE), allocator_(allocator), offset_(0) { memset(&mem_reqs, 0, sizeof(mem_reqs)); } @@ -64,5 +64,4 @@ private: VkMemoryRequirements mem_reqs; VulkanDeviceAllocator *allocator_; size_t offset_; - bool needStaging; }; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 22e9e91f58..107d667337 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -369,7 +369,6 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag } } - void DrawEngineVulkan::ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef) { // At this point, we know if the vertices are full alpha or not. // TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 1ade5640a7..5c619407fb 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1503,39 +1503,40 @@ void CreateImage(VulkanContext *vulkan, VKImage &img, int width, int height, VkF res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0); assert(res == VK_SUCCESS); + VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; ivci.format = ici.format; ivci.image = img.image; ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; - ivci.subresourceRange.aspectMask = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + ivci.subresourceRange.aspectMask = aspects; ivci.subresourceRange.layerCount = 1; ivci.subresourceRange.levelCount = 1; res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.view); assert(res == VK_SUCCESS); - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; + VkPipelineStageFlagBits dstStage; + VkAccessFlagBits dstAccessMask; switch (initialLayout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; break; } - barrier.newLayout = initialLayout; - barrier.subresourceRange.aspectMask = ivci.subresourceRange.aspectMask; - vkCmdPipelineBarrier(vulkan->GetInitCommandBuffer(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - img.layout = VK_IMAGE_LAYOUT_UNDEFINED; + + TransitionImageLayout2(vulkan->GetInitCommandBuffer(), img.image, aspects, + VK_IMAGE_LAYOUT_UNDEFINED, initialLayout, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, dstStage, + 0, dstAccessMask); } // A VKFramebuffer is a VkFramebuffer (note caps difference) plus all the textures it owns. From 804aa79376eb4cb216f9f7fa1b8015eff1d3ac59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 20 Oct 2017 18:09:05 +0200 Subject: [PATCH 041/111] Various Vulkan image transition fixes and related --- Common/Vulkan/VulkanContext.cpp | 10 ++-- Common/Vulkan/VulkanMemory.cpp | 8 +++- GPU/Vulkan/DrawEngineVulkan.cpp | 10 +++- GPU/Vulkan/DrawEngineVulkan.h | 2 +- GPU/Vulkan/PipelineManagerVulkan.cpp | 4 ++ GPU/Vulkan/PipelineManagerVulkan.h | 1 + GPU/Vulkan/StateMappingVulkan.cpp | 6 +-- Windows/GPU/WindowsVulkanContext.cpp | 12 +---- ext/native/thin3d/thin3d_vulkan.cpp | 72 +++++++++++++++------------- 9 files changed, 70 insertions(+), 55 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index daa21b05fc..c2861285b3 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -1203,7 +1203,7 @@ void VulkanContext::DestroyDevice() { VkPipelineCache VulkanContext::CreatePipelineCache() { VkPipelineCache cache; - VkPipelineCacheCreateInfo pc = { VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; + VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; pc.pInitialData = nullptr; pc.initialDataSize = 0; pc.flags = 0; @@ -1213,7 +1213,7 @@ VkPipelineCache VulkanContext::CreatePipelineCache() { } bool VulkanContext::CreateShaderModule(const std::vector &spirv, VkShaderModule *shaderModule) { - VkShaderModuleCreateInfo sm = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; + VkShaderModuleCreateInfo sm{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; sm.pCode = spirv.data(); sm.codeSize = spirv.size() * sizeof(uint32_t); sm.flags = 0; @@ -1229,7 +1229,7 @@ void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFla VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) { - VkImageMemoryBarrier image_memory_barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + VkImageMemoryBarrier image_memory_barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; image_memory_barrier.srcAccessMask = srcAccessMask; image_memory_barrier.dstAccessMask = dstAccessMask; image_memory_barrier.oldLayout = oldImageLayout; @@ -1237,8 +1237,8 @@ void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFla image_memory_barrier.image = image; image_memory_barrier.subresourceRange.aspectMask = aspectMask; image_memory_barrier.subresourceRange.baseMipLevel = 0; - image_memory_barrier.subresourceRange.levelCount = 1; - image_memory_barrier.subresourceRange.layerCount = 1; + image_memory_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + image_memory_barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp index c6db161c8a..03983c62a6 100644 --- a/Common/Vulkan/VulkanMemory.cpp +++ b/Common/Vulkan/VulkanMemory.cpp @@ -47,10 +47,16 @@ bool VulkanPushBuffer::AddBuffer() { return false; } + VkMemoryRequirements reqs; + vkGetBufferMemoryRequirements(device_, info.buffer, &reqs); + + // TODO: We really should use memoryTypeIndex here.. + // Okay, that's the buffer. Now let's allocate some memory for it. VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + // TODO: Should check here that memoryTypeIndex_ matches reqs.memoryTypeBits. alloc.memoryTypeIndex = memoryTypeIndex_; - alloc.allocationSize = size_; + alloc.allocationSize = reqs.size; res = vkAllocateMemory(device_, &alloc, nullptr, &info.deviceMemory); if (VK_SUCCESS != res) { diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 07e46aee86..50385236c4 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -884,8 +884,11 @@ void DrawEngineVulkan::DoFlush() { if (pipeline != lastPipeline_) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); lastPipeline_ = pipeline; + if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { + gstate_c.Dirty(DIRTY_BLEND_STATE); + } } - ApplyDrawStateLate(cmd, false, 0); + ApplyDrawStateLate(cmd, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); } lastPrim_ = prim; @@ -986,8 +989,11 @@ void DrawEngineVulkan::DoFlush() { if (pipeline != lastPipeline_) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw. lastPipeline_ = pipeline; + if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { + gstate_c.Dirty(DIRTY_BLEND_STATE); + } } - ApplyDrawStateLate(cmd, false, 0); + ApplyDrawStateLate(cmd, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); } lastPrim_ = prim; diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 1bb2d3295c..a5387dfb9d 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -171,7 +171,7 @@ public: private: struct FrameData; - void ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef); + void ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); void InitDeviceObjects(); diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index d23906750c..f0822a2d45 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -122,6 +122,8 @@ static bool UsesBlendConstant(int factor) { static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pipelineCache, VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) { + bool useBlendConstant = false; + VkPipelineColorBlendAttachmentState blend0 = {}; blend0.blendEnable = key.blendEnable; if (key.blendEnable) { @@ -166,6 +168,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip if (key.blendEnable && (UsesBlendConstant(key.srcAlpha) || UsesBlendConstant(key.srcColor) || UsesBlendConstant(key.destAlpha) || UsesBlendConstant(key.destColor))) { dynamicStates[numDyn++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS; + useBlendConstant = true; } dynamicStates[numDyn++] = VK_DYNAMIC_STATE_SCISSOR; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_VIEWPORT; @@ -285,6 +288,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip VulkanPipeline *vulkanPipeline = new VulkanPipeline(); vulkanPipeline->pipeline = pipeline; vulkanPipeline->uniformBlocks = UB_VS_FS_BASE; + vulkanPipeline->useBlendConstant = useBlendConstant; if (useHwTransform) { if (vs->HasLights()) { vulkanPipeline->uniformBlocks |= UB_VS_LIGHTS; diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 7ee6770869..15e29864f1 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -64,6 +64,7 @@ enum { struct VulkanPipeline { VkPipeline pipeline; int uniformBlocks; // UB_ enum above. + bool useBlendConstant; }; class VulkanContext; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 107d667337..dc390ef2af 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -369,7 +369,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag } } -void DrawEngineVulkan::ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef) { +void DrawEngineVulkan::ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant) { // At this point, we know if the vertices are full alpha or not. // TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? if (!gstate.isModeClear()) { @@ -401,9 +401,9 @@ void DrawEngineVulkan::ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencil if (applyStencilRef) { vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, stencilRef); } - if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { + if (gstate_c.IsDirty(DIRTY_BLEND_STATE) && useBlendConstant) { float bc[4]; Uint8x4ToFloat4(bc, dynState_.blendColor); vkCmdSetBlendConstants(cmd, bc); } -} \ No newline at end of file +} diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 6565ceee6e..f9dd032ed9 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -124,17 +124,7 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep } message << "[" << pLayerPrefix << "] " << ObjTypeToString(objType) << " Code " << msgCode << " : " << pMsg << "\n"; - if (msgCode == 2) // Useless perf warning - return false; - - // This seems like a bogus result when submitting two command buffers in one go, one creating the image, the other one using it. - if (msgCode == 6 && startsWith(pMsg, "Cannot submit cmd buffer using image")) - return false; - if (msgCode == 11) - return false; - // Silence "invalid reads of buffer data" - usually just uninitialized color buffers that will immediately get cleared due to our - // lacking clearing optimizations. - if (msgCode == 15 && objType == VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT) + if (msgCode == 2) // Useless perf warning ("Vertex attribute at location X not consumed by vertex shader") return false; #ifdef _WIN32 diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 5c619407fb..9b22558714 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1898,56 +1898,64 @@ void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPass // Now, if the image needs transitioning, let's transition. // The backbuffer does not, that's handled by VulkanContext. if (fb->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->color.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->color.image; - barrier.srcAccessMask = 0; + VkAccessFlagBits srcAccessMask; + VkPipelineStageFlagBits srcStage; switch (fb->color.layout) { case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_UNDEFINED: + srcAccessMask = (VkAccessFlagBits)0; + srcStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + break; + default: + assert(0); break; } - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - // TODO: Double-check these flags. Should be fine. - vkCmdPipelineBarrier(cmd_, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->color.layout = barrier.newLayout; + TransitionImageLayout2(cmd_, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + fb->color.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->depth.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->depth.image; - barrier.srcAccessMask = 0; + VkAccessFlagBits srcAccessMask; + VkPipelineStageFlagBits srcStage; switch (fb->depth.layout) { case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_UNDEFINED: + srcAccessMask = (VkAccessFlagBits)0; + srcStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + break; + default: + assert(0); break; } - barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - // TODO: Double-check these flags. Should be fine. - vkCmdPipelineBarrier(cmd_, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->depth.layout = barrier.newLayout; + TransitionImageLayout2(cmd_, fb->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + fb->depth.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } renderPass = renderPasses_[RPIndex(rp.color, rp.depth)]; From eabaf6ddcdb6938e2a57aff20d4eb5f3ce79bde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 21 Oct 2017 13:05:52 +0200 Subject: [PATCH 042/111] Bit more Vulkan barrier cleanup. --- Common/Vulkan/VulkanContext.cpp | 40 +++++++--------------------- Windows/GPU/WindowsVulkanContext.cpp | 2 ++ ext/native/thin3d/thin3d_vulkan.cpp | 34 +++++++++++------------ 3 files changed, 28 insertions(+), 48 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index c2861285b3..a5af3a8083 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -154,39 +154,17 @@ VulkanContext::~VulkanContext() { } void TransitionToPresent(VkCommandBuffer cmd, VkImage image) { - VkImageMemoryBarrier prePresentBarrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - prePresentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - prePresentBarrier.subresourceRange.baseMipLevel = 0; - prePresentBarrier.subresourceRange.levelCount = 1; - prePresentBarrier.subresourceRange.baseArrayLayer = 0; - prePresentBarrier.subresourceRange.layerCount = 1; - prePresentBarrier.image = image; - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - 0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier); + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT); } void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) { - VkImageMemoryBarrier prePresentBarrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - prePresentBarrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - prePresentBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - prePresentBarrier.subresourceRange.baseMipLevel = 0; - prePresentBarrier.subresourceRange.levelCount = 1; - prePresentBarrier.subresourceRange.baseArrayLayer = 0; - prePresentBarrier.subresourceRange.layerCount = 1; - prePresentBarrier.image = image; - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - 0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier); + TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); } VkCommandBuffer VulkanContext::GetInitCommandBuffer() { @@ -1239,6 +1217,8 @@ void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFla image_memory_barrier.subresourceRange.baseMipLevel = 0; image_memory_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; image_memory_barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index f9dd032ed9..e326e11c54 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -126,6 +126,8 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep if (msgCode == 2) // Useless perf warning ("Vertex attribute at location X not consumed by vertex shader") return false; + if (msgCode == 64) // Another useless perf warning that will be seen less and less as we optimize - vkCmdClearAttachments() issued on command buffer object 0x00000195296C6D40 prior to any Draw Cmds. It is recommended you use RenderPass LOAD_OP_CLEAR on Attachments prior to any Draw. + return false; #ifdef _WIN32 std::string msg = message.str(); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 9b22558714..a9005e17ac 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1787,6 +1787,8 @@ void VKContext::SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; img.layout = barrier.newLayout; } @@ -1816,6 +1818,8 @@ void VKContext::SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; img.layout = barrier.newLayout; } @@ -2008,29 +2012,23 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne transitionCmdBuf = vulkan_->GetInitCommandBuffer(); } - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->color.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->color.image; - barrier.srcAccessMask = 0; - switch (barrier.oldLayout) { + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; + switch (fb->color.layout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT|VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; } - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - - // we're between passes so it's OK. - // ARM Best Practices guide recommends these stage bits. - vkCmdPipelineBarrier(transitionCmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->color.layout = barrier.newLayout; + TransitionImageLayout2(transitionCmdBuf, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + srcAccessMask, VK_ACCESS_SHADER_READ_BIT); + fb->color.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { From 8f9bb590050c6b666e7d17938361db62ca902b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 22 Oct 2017 10:07:35 +0200 Subject: [PATCH 043/111] Vulkan: Fix silly mistake in blend constant checks, thanks Unknown. --- GPU/Vulkan/DrawEngineVulkan.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 50385236c4..c174e5898e 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -883,10 +883,10 @@ void DrawEngineVulkan::DoFlush() { } if (pipeline != lastPipeline_) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); - lastPipeline_ = pipeline; if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { gstate_c.Dirty(DIRTY_BLEND_STATE); } + lastPipeline_ = pipeline; } ApplyDrawStateLate(cmd, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); @@ -988,10 +988,10 @@ void DrawEngineVulkan::DoFlush() { } if (pipeline != lastPipeline_) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw. - lastPipeline_ = pipeline; if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { gstate_c.Dirty(DIRTY_BLEND_STATE); } + lastPipeline_ = pipeline; } ApplyDrawStateLate(cmd, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); From 27db1d558cbeb1e566c550529feb5d00a56af358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 22 Oct 2017 10:10:59 +0200 Subject: [PATCH 044/111] Comment and typo fixes. --- GPU/Common/FramebufferCommon.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 99f2632baf..266d48a480 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1837,6 +1837,15 @@ void FramebufferManagerCommon::ShowScreenResolution() { INFO_LOG(SYSTEM, "%s", messageStream.str().c_str()); } +// We might also want to implement an asynchronous callback-style version of this. Would probably +// only be possible to implement optimally on Vulkan, but on GL and D3D11 we could do pixel buffers +// and read on the next frame, then call the callback. PackFramebufferAsync_ on OpenGL already does something similar. +// +// The main use cases for this are: +// * GE debugging(in practice async will not matter because it will stall anyway.) +// * Video file recording(would probably be great if it was async.) +// * Screenshots(benefit slightly from async.) +// * Save state screenshots(could probably be async but need to manage the stall.) bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) { VirtualFramebuffer *vfb = currentRenderVfb_; if (!vfb) { @@ -1959,10 +1968,13 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { // This function takes an already correctly-sized framebuffer and packs it into RAM. // Does not need to account for scaling. -// Color conversion is currently done on CPU but should be done on GPU. +// Color conversion is currently done on CPU but should theoretically be done on GPU. +// (Except using the GPU might cause problems because of various implementations' +// dithering behavior and games that expect exact colors like Danganronpa, so we +// can't entirely be rid of the CPU path.) -- unknown void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { if (!vfb->fbo) { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferD3D11_: vfb->fbo == 0"); + ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0"); return; } From 916bb5436120a5c944946ed2eb586fff5b86e5bf Mon Sep 17 00:00:00 2001 From: Carlos Estrague / Mrc_munir Date: Sun, 22 Oct 2017 21:57:35 +0200 Subject: [PATCH 045/111] Separate -Bsymbolic Shared linker flag Using "$ {CMAKE_SHARED_LINKER_FLAGS}" for separate linker -Bsymbolic. Previously it ignored to the previous declared flags it does not find them. for this gave error with some linkers flags --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 820ac2c889..8cdbdff811 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1617,7 +1617,7 @@ add_library(${CoreLibName} ${CoreLinkType} if(ANDROID) set(CoreExtraLibs ${CoreExtraLibs} android) if(X86_64) - set(CoreExtraLibs ${CoreExtraLibs} android -Wl,-Bsymbolic) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic") endif() endif() From 2c8fb0fbd18ebaade6c4892f73e6baa1fc28f626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 24 Oct 2017 15:38:16 +0200 Subject: [PATCH 046/111] Can't use the camera stuff on Android versions before 11. Fixes #10029. --- GPU/GPUCommon.h | 4 ++-- android/src/org/ppsspp/ppsspp/NativeActivity.java | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 0eceaa0b4c..3d2430125d 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -183,7 +183,7 @@ public: std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override { return "N/A"; } - bool DescribeCodePtr(const u8 *ptr, std::string &name); + bool DescribeCodePtr(const u8 *ptr, std::string &name) override; std::vector ActiveDisplayLists() override; void ResetListPC(int listID, u32 pc) override; @@ -227,7 +227,7 @@ public: bool DecodeTexture(u8* dest, const GPUgstate &state) override { return false; } - std::vector GetFramebufferList(); + std::vector GetFramebufferList() override; void ClearShaderCache() override {} void CleanupBeforeUI() override {} diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 39adc3b0b2..b48cfae5c1 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -226,7 +226,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } break; case REQUEST_CODE_CAMERA_PERMISSION: - if (permissionsGranted(permissions, grantResults)) { + if (mCameraHelper != null && permissionsGranted(permissions, grantResults)) { mCameraHelper.startCamera(); } break; @@ -332,7 +332,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } mLocationHelper = new LocationHelper(this); - mCameraHelper = new CameraHelper(this); + if (Build.VERSION.SDK_INT >= 11) { + // android.graphics.SurfaceTexture is not available before version 11. + mCameraHelper = new CameraHelper(this); + } } @TargetApi(24) @@ -1209,10 +1212,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } } else if (command.equals("camera_command")) { if (params.equals("startVideo")) { - if (!askForPermissions(permissionsForCamera, REQUEST_CODE_CAMERA_PERMISSION)) { + if (mCameraHelper != null && !askForPermissions(permissionsForCamera, REQUEST_CODE_CAMERA_PERMISSION)) { mCameraHelper.startCamera(); } - } else if (params.equals("stopVideo")) { + } else if (mCameraHelper != null && params.equals("stopVideo")) { mCameraHelper.stopCamera(); } } From d1d1e1f742ab21ed9c5023724f46d03f4ab683a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 24 Oct 2017 22:38:26 +0200 Subject: [PATCH 047/111] Fix issue with caret position in text boxes caused by a bad hash function. Fletcher is 2-bytes-at-a-time and thus very inappropriate for short strings. --- ext/native/gfx_es2/draw_text_android.cpp | 8 +++---- ext/native/gfx_es2/draw_text_qt.cpp | 4 ++-- ext/native/gfx_es2/draw_text_win.cpp | 8 +++---- ext/native/util/hash/hash.cpp | 28 ------------------------ ext/native/util/hash/hash.h | 6 ++--- 5 files changed, 13 insertions(+), 41 deletions(-) diff --git a/ext/native/gfx_es2/draw_text_android.cpp b/ext/native/gfx_es2/draw_text_android.cpp index 9e229008f0..e01c9a5bb3 100644 --- a/ext/native/gfx_es2/draw_text_android.cpp +++ b/ext/native/gfx_es2/draw_text_android.cpp @@ -46,7 +46,7 @@ bool TextDrawerAndroid::IsReady() const { uint32_t TextDrawerAndroid::SetFont(const char *fontName, int size, int flags) { // We will only use the default font but just for consistency let's still involve // the font name. - uint32_t fontHash = hash::Fletcher((const uint8_t *)fontName, strlen(fontName)); + uint32_t fontHash = hash::Adler32((const uint8_t *)fontName, strlen(fontName)); fontHash ^= size; fontHash ^= flags << 10; @@ -79,7 +79,7 @@ std::string TextDrawerAndroid::NormalizeString(std::string str) { } void TextDrawerAndroid::MeasureString(const char *str, size_t len, float *w, float *h) { - uint32_t stringHash = hash::Fletcher((const uint8_t *)str, len); + uint32_t stringHash = hash::Adler32((const uint8_t *)str, len); uint32_t entryHash = stringHash ^ fontHash_; TextMeasureEntry *entry; @@ -129,7 +129,7 @@ void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bou float total_w = 0.0f; float total_h = 0.0f; for (size_t i = 0; i < lines.size(); i++) { - uint32_t stringHash = hash::Fletcher((const uint8_t *)&lines[i][0], lines[i].length()); + uint32_t stringHash = hash::Adler32((const uint8_t *)&lines[i][0], lines[i].length()); uint32_t entryHash = stringHash ^ fontHash_; TextMeasureEntry *entry; @@ -168,7 +168,7 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, int result = javaVM->GetEnv((void **)&env, JNI_VERSION_1_6); assert(env == env_); - uint32_t stringHash = hash::Fletcher((const uint8_t *)text.data(), text.size()); + uint32_t stringHash = hash::Adler32((const uint8_t *)text.data(), text.size()); uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24); target.Flush(true); diff --git a/ext/native/gfx_es2/draw_text_qt.cpp b/ext/native/gfx_es2/draw_text_qt.cpp index d0d8773dd8..7b523f47ab 100644 --- a/ext/native/gfx_es2/draw_text_qt.cpp +++ b/ext/native/gfx_es2/draw_text_qt.cpp @@ -24,7 +24,7 @@ TextDrawerQt::~TextDrawerQt() { uint32_t TextDrawerQt::SetFont(const char *fontName, int size, int flags) { // We will only use the default font - uint32_t fontHash = 0; //hash::Fletcher((const uint8_t *)fontName, strlen(fontName)); + uint32_t fontHash = 0; //hash::Adler32((const uint8_t *)fontName, strlen(fontName)); fontHash ^= size; fontHash ^= flags << 10; @@ -72,7 +72,7 @@ void TextDrawerQt::DrawString(DrawBuffer &target, const char *str, float x, floa if (!strlen(str)) return; - uint32_t stringHash = hash::Fletcher((const uint8_t *)str, strlen(str)); + uint32_t stringHash = hash::Adler32((const uint8_t *)str, strlen(str)); uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24); target.Flush(true); diff --git a/ext/native/gfx_es2/draw_text_win.cpp b/ext/native/gfx_es2/draw_text_win.cpp index 9df6ee09d0..4f0dd44e19 100644 --- a/ext/native/gfx_es2/draw_text_win.cpp +++ b/ext/native/gfx_es2/draw_text_win.cpp @@ -83,7 +83,7 @@ TextDrawerWin32::~TextDrawerWin32() { } uint32_t TextDrawerWin32::SetFont(const char *fontName, int size, int flags) { - uint32_t fontHash = fontName ? hash::Fletcher((const uint8_t *)fontName, strlen(fontName)) : 0; + uint32_t fontHash = fontName ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0; fontHash ^= size; fontHash ^= flags << 10; @@ -119,7 +119,7 @@ void TextDrawerWin32::SetFont(uint32_t fontHandle) { } void TextDrawerWin32::MeasureString(const char *str, size_t len, float *w, float *h) { - uint32_t stringHash = hash::Fletcher((const uint8_t *)str, len); + uint32_t stringHash = hash::Adler32((const uint8_t *)str, len); uint32_t entryHash = stringHash ^ fontHash_; TextMeasureEntry *entry; @@ -164,7 +164,7 @@ void TextDrawerWin32::MeasureStringRect(const char *str, size_t len, const Bound float total_w = 0.0f; float total_h = 0.0f; for (size_t i = 0; i < lines.size(); i++) { - uint32_t stringHash = hash::Fletcher((const uint8_t *)&lines[i][0], lines[i].length()); + uint32_t stringHash = hash::Adler32((const uint8_t *)&lines[i][0], lines[i].length()); uint32_t entryHash = stringHash ^ fontHash_; TextMeasureEntry *entry; @@ -197,7 +197,7 @@ void TextDrawerWin32::DrawString(DrawBuffer &target, const char *str, float x, f if (!strlen(str)) return; - uint32_t stringHash = hash::Fletcher((const uint8_t *)str, strlen(str)); + uint32_t stringHash = hash::Adler32((const uint8_t *)str, strlen(str)); uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24); target.Flush(true); diff --git a/ext/native/util/hash/hash.cpp b/ext/native/util/hash/hash.cpp index 3ebaea7d02..3bb840f84d 100644 --- a/ext/native/util/hash/hash.cpp +++ b/ext/native/util/hash/hash.cpp @@ -3,35 +3,7 @@ namespace hash { -// uint32_t -// WARNING - may read one more byte! Fine if the input is a null-terminated string. -// Implementation from Wikipedia. -uint32_t Fletcher(const uint8_t *data_uint8, size_t length) { - const uint16_t *data = (const uint16_t *)data_uint8; - size_t len = (length + 1) / 2; - uint32_t sum1 = 0xffff, sum2 = 0xffff; - - while (len) { - size_t tlen = len > 360 ? 360 : len; - len -= tlen; - - do { - sum1 += *data++; - sum2 += sum1; - } while (--tlen); - - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); - } - - /* Second reduction step to reduce sums to 16 bits */ - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); - return sum2 << 16 | sum1; -} - // Implementation from Wikipedia -// Slightly slower than Fletcher above, but slighly more reliable. #define MOD_ADLER 65521 // data: Pointer to the data to be summed; len is in bytes uint32_t Adler32(const uint8_t *data, size_t len) { diff --git a/ext/native/util/hash/hash.h b/ext/native/util/hash/hash.h index 0c7ac64326..2f7e4305b6 100644 --- a/ext/native/util/hash/hash.h +++ b/ext/native/util/hash/hash.h @@ -1,14 +1,14 @@ #ifndef _UTIL_HASH_HASH_H #define _UTIL_HASH_HASH_H -#include +#include #include "base/basictypes.h" namespace hash { -uint32_t Fletcher(const uint8_t *data_u8, size_t length); // FAST. Length & 1 == 0. -uint32_t Adler32(const uint8_t *data, size_t len); // Fairly accurate, slightly slower +// Fairly decent function for hashing strings. +uint32_t Adler32(const uint8_t *data, size_t len); } // namespace hash From 7b1e1cd5e0a47a2133a75ad7d9ffe88a9f5b53e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 24 Oct 2017 23:05:21 +0200 Subject: [PATCH 048/111] Automatically scroll textboxes when the caret ends up outside. Fixes #10026 . There was some code for this before but it didn't work. --- ext/native/ui/view.cpp | 13 +++++++------ ext/native/ui/view.h | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ext/native/ui/view.cpp b/ext/native/ui/view.cpp index 1f99d2b150..65f1aab6fe 100644 --- a/ext/native/ui/view.cpp +++ b/ext/native/ui/view.cpp @@ -783,7 +783,7 @@ void TextEdit::Draw(UIContext &dc) { float w, h; Bounds textBounds = bounds_; - textBounds.x = textX; + textBounds.x = textX - scrollPos_; if (text_.empty()) { if (placeholderText_.size()) { @@ -797,13 +797,14 @@ void TextEdit::Draw(UIContext &dc) { if (HasFocus()) { // Hack to find the caret position. Might want to find a better way... dc.MeasureTextCount(dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), caret_, &w, &h, ALIGN_VCENTER | ALIGN_LEFT); - float caretX = w; - caretX += textX; - + float caretX = w - scrollPos_; if (caretX > bounds_.w) { - // Scroll text to the left if the caret won't fit. Not ideal but looks better than not scrolling it. - textX -= caretX - bounds_.w; + scrollPos_ += caretX - bounds_.w; } + if (caretX < 0) { + scrollPos_ += caretX; + } + caretX += textX; dc.FillRect(UI::Drawable(textColor), Bounds(caretX - 1, bounds_.y + 2, 3, bounds_.h - 4)); } dc.PopScissor(); diff --git a/ext/native/ui/view.h b/ext/native/ui/view.h index aada63d9ff..93afab7c18 100644 --- a/ext/native/ui/view.h +++ b/ext/native/ui/view.h @@ -780,7 +780,7 @@ private: class TextEdit : public View { public: TextEdit(const std::string &text, const std::string &placeholderText, LayoutParams *layoutParams = 0); - void SetText(const std::string &text) { text_ = text; caret_ = (int)text_.size(); } + void SetText(const std::string &text) { text_ = text; scrollPos_ = 0; caret_ = (int)text_.size(); } void SetTextColor(uint32_t color) { textColor_ = color; hasTextColor_ = true; } const std::string &GetText() const { return text_; } void SetMaxLen(size_t maxLen) { maxLen_ = maxLen; } @@ -802,6 +802,7 @@ private: uint32_t textColor_; bool hasTextColor_ = false; int caret_; + int scrollPos_ = 0; size_t maxLen_; bool ctrlDown_ = false; // TODO: Make some global mechanism for this. // TODO: Selections From a33daa4985315dca91de78f1b3010e0dc62d754a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 10:48:41 +0200 Subject: [PATCH 049/111] Update gradle files to Android Studio 3.0 defaults. --- android/AndroidManifest.xml | 2 +- android/build.gradle | 6 +++--- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 34c483eab2..8ef8b5c6a5 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -8,7 +8,7 @@ + android:targetSdkVersion="26" /> diff --git a/android/build.gradle b/android/build.gradle index b49ccd51db..097a02e654 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -35,8 +35,8 @@ android { } } } - compileSdkVersion 25 - buildToolsVersion '25.0.0' + compileSdkVersion 26 + buildToolsVersion '27.0.0' defaultConfig { applicationId 'org.ppsspp.ppsspp' if (androidGitVersion.name() != "unknown" && androidGitVersion.code() >= 14000000) { @@ -52,7 +52,7 @@ android { new File("versioncode.txt").write(androidGitVersion.code().toString()) minSdkVersion 9 - targetSdkVersion 25 + targetSdkVersion 26 if (project.hasProperty("ANDROID_VERSION_CODE") && project.hasProperty("ANDROID_VERSION_NAME")) { versionCode ANDROID_VERSION_CODE versionName ANDROID_VERSION_NAME diff --git a/build.gradle b/build.gradle index e994947a78..62b62b545c 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:3.0.0' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 063599a2d7..1f02b5e381 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Mar 13 14:55:51 CET 2017 +#Thu Oct 26 10:42:05 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip From 84ed793adf7d15a20b346c421df0f81417661f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 16 Aug 2017 23:03:30 +0200 Subject: [PATCH 050/111] Initial work on render queue manager --- Common/Common.vcxproj | 2 + Common/Common.vcxproj.filters | 4 +- Common/Vulkan/VulkanRenderManager.cpp | 67 +++++++++++++ Common/Vulkan/VulkanRenderManager.h | 131 ++++++++++++++++++++++++++ ext/native/thin3d/thin3d.h | 1 + ext/native/thin3d/thin3d_vulkan.cpp | 6 ++ 6 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 Common/Vulkan/VulkanRenderManager.cpp create mode 100644 Common/Vulkan/VulkanRenderManager.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 86758c4229..58a8fac99a 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -249,6 +249,7 @@ + @@ -320,6 +321,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 0251f2b334..c3ae5eea9e 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -75,6 +75,7 @@ + @@ -138,6 +139,7 @@ + @@ -153,4 +155,4 @@ {c14d66ef-5f7c-4565-975a-72774e7ccfb9} - \ No newline at end of file + diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp new file mode 100644 index 0000000000..bbe821eadf --- /dev/null +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -0,0 +1,67 @@ +#include "VulkanRenderManager.h" + +void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { + // If this is the first drawing command, merge it into the pass. + if (curRp_->numDraws == 0) { + curRp_->clearColor = clearColor; + curRp_->clearZ = clearZ; + curRp_->clearStencil = clearStencil; + curRp_->clearMask = clearMask; + } else { + VkRenderData data{ VKR_CLEAR }; + data.clear.clearColor = clearColor; + data.clear.clearZ = clearZ; + data.clear.clearStencil = clearStencil; + data.clear.clearMask = clearMask; + curRp_->commands.push_back(data); + } +} + +void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { + // Optimizes renderpasses, then sequences them. + for (int i = 0; i < renderPasses_.size(); i++) { + auto &commands = renderPasses_[i]->commands; + for (const auto &c : commands) { + switch (c.cmd) { + case VKR_VIEWPORT: + vkCmdSetViewport(cmdbuf, 0, 1, &c.viewport.vp); + break; + + case VKR_SCISSOR: + vkCmdSetScissor(cmdbuf, 0, 1, &c.scissor.scissor); + break; + + case VKR_DRAW_INDEXED: + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); + vkCmdBindIndexBuffer(cmdbuf, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + vkCmdDrawIndexed(cmdbuf, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); + break; + + case VKR_DRAW: + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + vkCmdDraw(cmdbuf, c.drawIndexed.count, c.drawIndexed.instances, 0, 0); + break; + + case VKR_STENCIL: + vkCmdSetStencilWriteMask(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); + vkCmdSetStencilCompareMask(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); + vkCmdSetStencilReference(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); + break; + + case VKR_BLEND: + vkCmdSetBlendConstants(cmdbuf, c.blendColor.color); + break; + + case VKR_CLEAR: + // vkCmdClearAttachments + break; + } + } + } +} + +void VulkanRenderManager::Sync(VkCommandBuffer cmd) { + +} diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h new file mode 100644 index 0000000000..2f724ef627 --- /dev/null +++ b/Common/Vulkan/VulkanRenderManager.h @@ -0,0 +1,131 @@ +#pragma once + +#include + +#include "Common/Vulkan/VulkanContext.h" +#include "thin3d/thin3d.h" + +// Takes the role that a GL driver does of sequencing and optimizing render passes. +// Only draws and binds are handled here, resource creation and allocations are handled as normal. + + +enum VkRenderCmd : uint8_t { + VKR_STENCIL, + VKR_BLEND, + VKR_VIEWPORT, + VKR_SCISSOR, + VKR_CLEAR, + VKR_DRAW, + VKR_DRAW_INDEXED, +}; + +struct VkRenderData { + VkRenderCmd cmd; + union { + struct { + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + VkDescriptorSet ds; + int numUboOffsets; + uint32_t uboOffsets[3]; + VkBuffer vbuffer; + int offset; + int count; + } draw; + struct { + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + VkDescriptorSet ds; + int numUboOffsets; + uint32_t uboOffsets[3]; + VkBuffer vbuffer; // might need to increase at some point + VkDeviceSize voffset; + VkBuffer ibuffer; + VkDeviceSize ioffset; + int16_t count; + int16_t instances; + } drawIndexed; + struct { + uint32_t clearColor; + float clearZ; + int clearStencil; + int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc + } clear; + + struct { + VkViewport vp; + } viewport; + struct { + VkRect2D scissor; + } scissor; + struct { + uint8_t stencilWriteMask; + uint8_t stencilCompareMask; + uint8_t stencilRef; + } stencil; + struct { + float color[4]; + } blendColor; + struct { + + } beginRp; + struct { + + } endRp; + }; +}; + +struct VKRRenderPass { + VkFramebuffer framebuffer; + uint32_t clearColor; + float clearZ; + int clearStencil; + int clearMask = 0; // VK_IMAGE_ASPECT_COLOR_BIT etc + int dontCareMask = 0; + int numDraws; + std::vector commands; +}; + +class VulkanRenderManager { +public: + void SetViewport(VkViewport vp) { + VkRenderData data{ VKR_VIEWPORT }; + data.viewport.vp = vp; + curRp_->commands.push_back(data); + } + + void SetScissor(VkRect2D rc) { + VkRenderData data{ VKR_SCISSOR }; + data.scissor.scissor = rc; + curRp_->commands.push_back(data); + } + + void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); + + void Draw(VkPipeline pipeline, VkBuffer vbuffer, int offset, int count) { + VkRenderData data{ VKR_DRAW }; + data.draw.vbuffer = vbuffer; + data.draw.offset = offset; + curRp_->commands.push_back(data); + curRp_->numDraws++; + } + + void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count) { + VkRenderData data{ VKR_DRAW }; + data.drawIndexed.pipeline = pipeline; + data.drawIndexed.vbuffer = vbuffer; + data.drawIndexed.voffset = voffset; + data.drawIndexed.ibuffer = ibuffer; + data.drawIndexed.ioffset = ioffset; + curRp_->commands.push_back(data); + curRp_->numDraws++; + } + + void Flush(VkCommandBuffer cmd); + + // Bad for performance but sometimes necessary for synchonous CPU readbacks (screenshots and whatnot). + void Sync(VkCommandBuffer cmd); + + std::vector renderPasses_; + VKRRenderPass *curRp_; +}; \ No newline at end of file diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 78c8ea6b2b..dcbe746160 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -325,6 +325,7 @@ enum class NativeObject { CURRENT_RENDERPASS, RENDERPASS_COMMANDBUFFER, BOUND_TEXTURE_IMAGEVIEW, + RENDER_MANAGER, }; enum FBColorDepth { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index a9005e17ac..4e13dd3b36 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -34,6 +34,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanImage.h" #include "Common/Vulkan/VulkanMemory.h" +#include "Common/Vulkan/VulkanRenderManager.h" // We use a simple descriptor set for all rendering: 1 sampler, 1 texture, 1 UBO binding point. // binding 0 - uniform data @@ -472,6 +473,9 @@ public: return (uintptr_t)cmd_; case NativeObject::BOUND_TEXTURE_IMAGEVIEW: return (uintptr_t)boundImageView_[0]; + + case NativeObject::RENDER_MANAGER: + return renderManager_; default: return 0; } @@ -491,6 +495,8 @@ private: VulkanContext *vulkan_ = nullptr; + VulkanRenderManager renderManager_; + VKPipeline *curPipeline_ = nullptr; VKBuffer *curVBuffers_[4]{}; int curVBufferOffsets_[4]{}; From 417b96a1b07727887050563f9bf4d7348580248d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 18 Aug 2017 15:08:40 +0200 Subject: [PATCH 051/111] Use RenderManager for thin3d stuff --- Common/Vulkan/VulkanRenderManager.cpp | 57 +++++++++++++++++-- Common/Vulkan/VulkanRenderManager.h | 53 +++++++++++++++--- ext/native/thin3d/thin3d_vulkan.cpp | 81 +++++++++------------------ 3 files changed, 123 insertions(+), 68 deletions(-) diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp index bbe821eadf..e097d44eca 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -1,5 +1,18 @@ +#include "base/logging.h" + #include "VulkanRenderManager.h" +void VulkanRenderManager::BeginFrameWrites() { + vulkan_->BeginFrame(); + renderPasses_.push_back(new VKRRenderPass); + curRp_ = renderPasses_.back(); +} + +void VulkanRenderManager::EndFrame() { + vulkan_->EndFrame(); +} + + void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { // If this is the first drawing command, merge it into the pass. if (curRp_->numDraws == 0) { @@ -32,6 +45,7 @@ void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { break; case VKR_DRAW_INDEXED: + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); vkCmdBindIndexBuffer(cmdbuf, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); @@ -39,9 +53,10 @@ void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { break; case VKR_DRAW: - vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); - vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); - vkCmdDraw(cmdbuf, c.drawIndexed.count, c.drawIndexed.instances, 0, 0); + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.draw.vbuffer, &c.draw.voffset); + vkCmdDraw(cmdbuf, c.draw.count, 1, 0, 0); break; case VKR_STENCIL: @@ -55,11 +70,45 @@ void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { break; case VKR_CLEAR: - // vkCmdClearAttachments + { + int numAttachments = 0; + VkClearRect rc{}; + rc.baseArrayLayer = 0; + rc.layerCount = 1; + rc.rect.extent.width = curWidth_; + rc.rect.extent.height = curHeight_; + VkClearAttachment attachments[2]; + if (c.clear.clearMask & VK_IMAGE_ASPECT_COLOR_BIT) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + attachment.colorAttachment = 0; + Uint8x4ToFloat4(attachment.clearValue.color.float32, c.clear.clearColor); + } + if (c.clear.clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = 0; + if (c.clear.clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + attachment.clearValue.depthStencil.depth = c.clear.clearZ; + attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (c.clear.clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + attachment.clearValue.depthStencil.stencil = c.clear.clearStencil; + attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } + if (numAttachments) { + vkCmdClearAttachments(cmdbuf, numAttachments, attachments, 1, &rc); + } break; } + default: + ELOG("Unimpl queue command"); + ; + } } + delete renderPasses_[i]; } + renderPasses_.clear(); } void VulkanRenderManager::Sync(VkCommandBuffer cmd) { diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h index 2f724ef627..c4c59485eb 100644 --- a/Common/Vulkan/VulkanRenderManager.h +++ b/Common/Vulkan/VulkanRenderManager.h @@ -1,13 +1,17 @@ #pragma once #include +#include #include "Common/Vulkan/VulkanContext.h" +#include "math/dataconv.h" #include "thin3d/thin3d.h" // Takes the role that a GL driver does of sequencing and optimizing render passes. -// Only draws and binds are handled here, resource creation and allocations are handled as normal. +// Only draws and binds are handled here, resource creation and allocations are handled as normal - +// that's the nice thing with Vulkan. +// The cool thing is that you can Flush on a different thread than you record the commands on! enum VkRenderCmd : uint8_t { VKR_STENCIL, @@ -29,7 +33,7 @@ struct VkRenderData { int numUboOffsets; uint32_t uboOffsets[3]; VkBuffer vbuffer; - int offset; + VkDeviceSize voffset; int count; } draw; struct { @@ -44,6 +48,7 @@ struct VkRenderData { VkDeviceSize ioffset; int16_t count; int16_t instances; + VkIndexType indexType; } drawIndexed; struct { uint32_t clearColor; @@ -88,44 +93,76 @@ struct VKRRenderPass { class VulkanRenderManager { public: - void SetViewport(VkViewport vp) { + VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) {} + + // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. + void BeginFrameWrites(); + void EndFrame(); + + void SetViewport(const VkViewport &vp) { VkRenderData data{ VKR_VIEWPORT }; data.viewport.vp = vp; curRp_->commands.push_back(data); } - void SetScissor(VkRect2D rc) { + void SetScissor(const VkRect2D &rc) { VkRenderData data{ VKR_SCISSOR }; data.scissor.scissor = rc; curRp_->commands.push_back(data); } + void SetBlendFactor(float color[4]) { + VkRenderData data{ VKR_BLEND }; + CopyFloat4(data.blendColor.color, color); + curRp_->commands.push_back(data); + } + void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); - void Draw(VkPipeline pipeline, VkBuffer vbuffer, int offset, int count) { + void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { VkRenderData data{ VKR_DRAW }; + data.draw.count = count; + data.draw.pipeline = pipeline; + data.draw.pipelineLayout = layout; + data.draw.ds = descSet; data.draw.vbuffer = vbuffer; - data.draw.offset = offset; + data.draw.voffset = voffset; + data.draw.numUboOffsets = numUboOffsets; + for (int i = 0; i < numUboOffsets; i++) + data.draw.uboOffsets[i] = uboOffsets[i]; curRp_->commands.push_back(data); curRp_->numDraws++; } - void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count) { + void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, VkIndexType indexType) { VkRenderData data{ VKR_DRAW }; + data.drawIndexed.count = count; data.drawIndexed.pipeline = pipeline; + data.drawIndexed.pipelineLayout = layout; + data.drawIndexed.ds = descSet; data.drawIndexed.vbuffer = vbuffer; data.drawIndexed.voffset = voffset; data.drawIndexed.ibuffer = ibuffer; data.drawIndexed.ioffset = ioffset; + data.drawIndexed.numUboOffsets = numUboOffsets; + for (int i = 0; i < numUboOffsets; i++) + data.drawIndexed.uboOffsets[i] = uboOffsets[i]; + data.drawIndexed.indexType = indexType; curRp_->commands.push_back(data); curRp_->numDraws++; } + // Can run on a different thread! Just make sure to use BeginFrameWrites. void Flush(VkCommandBuffer cmd); - // Bad for performance but sometimes necessary for synchonous CPU readbacks (screenshots and whatnot). + // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(VkCommandBuffer cmd); std::vector renderPasses_; VKRRenderPass *curRp_; + +private: + VulkanContext *vulkan_; + int curWidth_; + int curHeight_; }; \ No newline at end of file diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 4e13dd3b36..6620921b43 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -135,6 +135,7 @@ static const VkStencilOp stencilOpToVK[8] = { VK_STENCIL_OP_DECREMENT_AND_WRAP, }; +// TODO: Replace with the one from dataconv static inline void Uint8x4ToFloat4(uint32_t u, float f[4]) { f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f); f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f); @@ -475,7 +476,7 @@ public: return (uintptr_t)boundImageView_[0]; case NativeObject::RENDER_MANAGER: - return renderManager_; + return (uintptr_t)&renderManager_; default: return 0; } @@ -701,7 +702,7 @@ bool VKTexture::Create(const TextureDesc &desc) { } VKContext::VKContext(VulkanContext *vulkan) - : viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan), frameNum_(0), caps_{} { + : viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan), frameNum_(0), caps_{}, renderManager_(vulkan) { caps_.anisoSupported = vulkan->GetFeaturesAvailable().samplerAnisotropy != 0; caps_.geometryShaderSupported = vulkan->GetFeaturesAvailable().geometryShader != 0; caps_.tesselationShaderSupported = vulkan->GetFeaturesAvailable().tessellationShader != 0; @@ -892,7 +893,7 @@ VkCommandBuffer VKContext::AllocCmdBuf() { } void VKContext::BeginFrame() { - vulkan_->BeginFrame(); + renderManager_.BeginFrameWrites(); FrameData &frame = frame_[frameNum_]; frame.startCmdBufs_ = 0; @@ -920,6 +921,7 @@ void VKContext::WaitRenderCompletion(Framebuffer *fbo) { void VKContext::EndFrame() { EndCurrentRenderpass(); + renderManager_.Flush(cmd_); if (cmd_) Crash(); @@ -934,7 +936,8 @@ void VKContext::EndFrame() { // Stop collecting data in the frame's data pushbuffer. push_->End(); - vulkan_->EndFrame(); + + renderManager_.EndFrame(); frameNum_++; if (frameNum_ >= vulkan_->GetInflightFrames()) @@ -1109,16 +1112,16 @@ void VKContext::SetViewports(int count, Viewport *viewports) { } void VKContext::SetBlendFactor(float color[4]) { - vkCmdSetBlendConstants(cmd_, color); + renderManager_.SetBlendFactor(color); } void VKContext::ApplyDynamicState() { if (scissorDirty_) { - vkCmdSetScissor(cmd_, 0, 1, &scissor_); + renderManager_.SetScissor(scissor_); scissorDirty_ = false; } if (viewportDirty_) { - vkCmdSetViewport(cmd_, 0, 1, &viewport_); + renderManager_.SetViewport(viewport_); viewportDirty_ = false; } } @@ -1303,13 +1306,9 @@ void VKContext::Draw(int vertexCount, int offset) { uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), &vulkanVbuf); - vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline); VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset); - VkBuffer buffers[1] = { vulkanVbuf }; - VkDeviceSize offsets[1] = { vbBindOffset }; - vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets); - vkCmdDraw(cmd_, vertexCount, 1, offset, 0); + + renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } void VKContext::DrawIndexed(int vertexCount, int offset) { @@ -1323,17 +1322,9 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), &vulkanVbuf); size_t ibBindOffset = push_->Push(ibuf->GetData(), ibuf->GetSize(), &vulkanIbuf); - vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline); - VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset); - VkBuffer buffers[1] = { vulkanVbuf }; - VkDeviceSize offsets[1] = { vbBindOffset }; - vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets); - - vkCmdBindIndexBuffer(cmd_, vulkanIbuf, ibBindOffset, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(cmd_, vertexCount, 1, 0, offset, 0); + renderManager_.DrawIndexed(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, VK_INDEX_TYPE_UINT32); } void VKContext::DrawUP(const void *vdata, int vertexCount) { @@ -1343,52 +1334,30 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { size_t vbBindOffset = push_->Push(vdata, vertexCount * curPipeline_->stride[0], &vulkanVbuf); uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); - vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline); VkBuffer buffers[1] = { vulkanVbuf }; VkDeviceSize offsets[1] = { vbBindOffset }; - vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets); - VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset); - vkCmdDraw(cmd_, vertexCount, 1, 0, 0); + + renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, vbBindOffset, vertexCount); } // TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. -void VKContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) { +void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) { if (!curRenderPass_) { ELOG("Clear: Need an active render pass"); return; } - int numAttachments = 0; - VkClearRect rc{}; - rc.baseArrayLayer = 0; - rc.layerCount = 1; - rc.rect.extent.width = curWidth_; - rc.rect.extent.height = curHeight_; - VkClearAttachment attachments[2]; - if (mask & FBChannel::FB_COLOR_BIT) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - attachment.colorAttachment = 0; - Uint8x4ToFloat4(colorval, attachment.clearValue.color.float32); - } - if (mask & (FBChannel::FB_DEPTH_BIT | FBChannel::FB_STENCIL_BIT)) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = 0; - if (mask & FBChannel::FB_DEPTH_BIT) { - attachment.clearValue.depthStencil.depth = depthVal; - attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (mask & FBChannel::FB_STENCIL_BIT) { - attachment.clearValue.depthStencil.stencil = stencilVal; - attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } - if (numAttachments) { - vkCmdClearAttachments(cmd_, numAttachments, attachments, 1, &rc); - } + int mask = 0; + if (clearMask & FBChannel::FB_COLOR_BIT) + mask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (clearMask & FBChannel::FB_DEPTH_BIT) + mask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (clearMask & FBChannel::FB_STENCIL_BIT) + mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + + renderManager_.Clear(colorval, depthVal, stencilVal, mask); } DrawContext *T3DCreateVulkanContext(VulkanContext *vulkan) { From 0a0494ef8ed7d9fedd43e05cc85094cba209378a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 19 Aug 2017 17:32:10 +0200 Subject: [PATCH 052/111] It builds! With some shortcuts, of course. --- Common/Vulkan/VulkanContext.cpp | 439 +--------- Common/Vulkan/VulkanContext.h | 99 +-- Common/Vulkan/VulkanImage.cpp | 17 +- Common/Vulkan/VulkanImage.h | 10 +- Common/Vulkan/VulkanRenderManager.cpp | 1133 +++++++++++++++++++++++-- Common/Vulkan/VulkanRenderManager.h | 266 +++++- GPU/Vulkan/DrawEngineVulkan.cpp | 27 +- GPU/Vulkan/DrawEngineVulkan.h | 13 +- GPU/Vulkan/FramebufferVulkan.cpp | 53 +- GPU/Vulkan/TextureCacheVulkan.cpp | 13 +- GPU/Vulkan/VulkanUtil.cpp | 11 +- GPU/Vulkan/VulkanUtil.h | 7 +- Windows/GPU/WindowsVulkanContext.cpp | 6 +- ext/native/math/dataconv.h | 11 + ext/native/thin3d/thin3d.h | 5 +- ext/native/thin3d/thin3d_d3d11.cpp | 2 +- ext/native/thin3d/thin3d_d3d9.cpp | 2 +- ext/native/thin3d/thin3d_gl.cpp | 2 +- ext/native/thin3d/thin3d_vulkan.cpp | 810 ++---------------- 19 files changed, 1462 insertions(+), 1464 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index a5af3a8083..20ec8b0bd3 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -167,120 +167,14 @@ void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) { VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); } -VkCommandBuffer VulkanContext::GetInitCommandBuffer() { +void VulkanContext::BeginFrame() { FrameData *frame = &frame_[curFrame_]; - if (!frame->hasInitCommands) { - VulkanBeginCommandBuffer(frame->cmdInit); - frame->hasInitCommands = true; - } - return frame_[curFrame_].cmdInit; -} - -void VulkanContext::QueueBeforeSurfaceRender(VkCommandBuffer cmd) { - cmdQueue_.push_back(cmd); -} - -VkCommandBuffer VulkanContext::BeginFrame() { - FrameData *frame = &frame_[curFrame_]; - // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. - // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device_, swap_chain_, UINT64_MAX, acquireSemaphore_, VK_NULL_HANDLE, ¤t_buffer); - // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR - // return codes - assert(res == VK_SUCCESS); - - // Make sure the very last command buffer from the frame before the previous has been fully executed. - WaitAndResetFence(frame->fence); - // Process pending deletes. frame->deleteList.PerformDeletes(device_); - - // Reset both command buffers in one fell swoop. - // Note that on the first frame, there might already be commands so don't reset in that case. - if (!frame->hasInitCommands) { - vkResetCommandPool(device_, frame->cmdPool, 0); - } - - VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin.pInheritanceInfo = nullptr; - res = vkBeginCommandBuffer(frame->cmdBuf, &begin); - - TransitionFromPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image); - return frame->cmdBuf; -} - -VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[2]) { - FrameData *frame = &frame_[curFrame_]; - VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - rp_begin.renderPass = surface_render_pass_; - rp_begin.framebuffer = framebuffers_[current_buffer]; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = width_; - rp_begin.renderArea.extent.height = height_; - rp_begin.clearValueCount = 2; - rp_begin.pClearValues = clear_values; - vkCmdBeginRenderPass(frame->cmdBuf, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); - return frame->cmdBuf; -} - -void VulkanContext::EndSurfaceRenderPass() { - FrameData *frame = &frame_[curFrame_]; - // ILOG("VulkanContext::EndSurfaceRenderPass"); - vkCmdEndRenderPass(frame->cmdBuf); } void VulkanContext::EndFrame() { - FrameData *frame = &frame_[curFrame_]; - TransitionToPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image); - - VkResult res = vkEndCommandBuffer(frame->cmdBuf); - assert(res == VK_SUCCESS); - - // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. - // This way we bunch up all the initialization needed for the frame, we render to - // other buffers before the back buffer, and then last we render to the backbuffer. - - int numCmdBufs = 0; - std::vector cmdBufs; - if (frame->hasInitCommands) { - vkEndCommandBuffer(frame->cmdInit); - cmdBufs.push_back(frame->cmdInit); - frame->hasInitCommands = false; - } - for (auto cmd : cmdQueue_) { - cmdBufs.push_back(cmd); - } - cmdQueue_.clear(); - cmdBufs.push_back(frame->cmdBuf); - - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &acquireSemaphore_; - VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; - submit_info.pWaitDstStageMask = waitStage; - submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); - submit_info.pCommandBuffers = cmdBufs.data(); - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; - res = vkQueueSubmit(gfx_queue_, 1, &submit_info, frame->fence); - assert(res == VK_SUCCESS); - - VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; - present.swapchainCount = 1; - present.pSwapchains = &swap_chain_; - present.pImageIndices = ¤t_buffer; - present.pWaitSemaphores = &renderingCompleteSemaphore_; - present.waitSemaphoreCount = 1; - present.pResults = NULL; - - res = vkQueuePresentKHR(gfx_queue_, &present); - // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI - // return codes - assert(!res); - - frame->deleteList.Take(globalDeleteList_); + frame_[curFrame_].deleteList.Take(globalDeleteList_); curFrame_++; if (curFrame_ >= inflightFrames_) { curFrame_ = 0; @@ -308,72 +202,19 @@ bool VulkanContext::MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirem return false; } -void VulkanBeginCommandBuffer(VkCommandBuffer cmd) { - VkResult U_ASSERT_ONLY res; - VkCommandBufferBeginInfo cmd_buf_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - cmd_buf_info.pInheritanceInfo = nullptr; - cmd_buf_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - res = vkBeginCommandBuffer(cmd, &cmd_buf_info); - assert(res == VK_SUCCESS); -} - -bool VulkanContext::InitObjects(bool depthPresent) { - int physical_device = 0; // TODO +bool VulkanContext::InitObjects() { InitQueue(); - // Create frame data - for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - VkResult U_ASSERT_ONLY res; - VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; - cmd_pool_info.queueFamilyIndex = graphics_queue_family_index_; - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; - - res = vkCreateCommandPool(device_, &cmd_pool_info, NULL, &frame_[i].cmdPool); - assert(res == VK_SUCCESS); - - VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - cmd_alloc.commandPool = frame_[i].cmdPool; - cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd_alloc.commandBufferCount = 2; - - VkCommandBuffer cmdBuf[2]; - res = vkAllocateCommandBuffers(device_, &cmd_alloc, cmdBuf); - assert(res == VK_SUCCESS); - frame_[i].cmdBuf = cmdBuf[0]; - frame_[i].cmdInit = cmdBuf[1]; - frame_[i].fence = CreateFence(true); // So it can be instantly waited on - } - - VkCommandBuffer cmd = GetInitCommandBuffer(); - if (!InitSwapchain(cmd)) { + if (!InitSwapchain()) { return false; } - InitDepthStencilBuffer(cmd); - - InitSurfaceRenderPass(depthPresent, true); - InitFramebuffers(depthPresent); - - // The init command buffer will be executed as part of the first frame. - return true; } void VulkanContext::DestroyObjects() { - for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - VkCommandBuffer cmdBuf[2]; - cmdBuf[0] = frame_[i].cmdBuf; - cmdBuf[1] = frame_[i].cmdInit; - vkFreeCommandBuffers(device_, frame_[i].cmdPool, 2, cmdBuf); - vkDestroyCommandPool(device_, frame_[i].cmdPool, nullptr); - } - for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { - vkDestroyFence(device_, frame_[i].fence, nullptr); - } - - DestroyFramebuffers(); - DestroySurfaceRenderPass(); - DestroyDepthStencilBuffer(); - DestroySwapChain(); + if (swapchain_ != VK_NULL_HANDLE) + vkDestroySwapchainKHR(device_, swapchain_, nullptr); + swapchain_ = VK_NULL_HANDLE; // If there happen to be any pending deletes, now is a good time. for (int i = 0; i < ARRAY_SIZE(frame_); i++) { @@ -670,78 +511,6 @@ void VulkanContext::DestroyDebugMsgCallback() { } } -void VulkanContext::InitDepthStencilBuffer(VkCommandBuffer cmd) { - VkResult U_ASSERT_ONLY res; - bool U_ASSERT_ONLY pass; - - const VkFormat depth_format = deviceInfo_.preferredDepthStencilFormat; - int aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.format = depth_format; - image_info.extent.width = width_; - image_info.extent.height = height_; - image_info.extent.depth = 1; - image_info.mipLevels = 1; - image_info.arrayLayers = 1; - image_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_info.queueFamilyIndexCount = 0; - image_info.pQueueFamilyIndices = NULL; - image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - image_info.flags = 0; - - VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - mem_alloc.allocationSize = 0; - mem_alloc.memoryTypeIndex = 0; - - VkMemoryRequirements mem_reqs; - - depth.format = depth_format; - - res = vkCreateImage(device_, &image_info, NULL, &depth.image); - assert(res == VK_SUCCESS); - - vkGetImageMemoryRequirements(device_, depth.image, &mem_reqs); - - mem_alloc.allocationSize = mem_reqs.size; - /* Use the memory properties to determine the type of memory required */ - pass = MemoryTypeFromProperties(mem_reqs.memoryTypeBits, - 0, /* No requirements */ - &mem_alloc.memoryTypeIndex); - assert(pass); - - res = vkAllocateMemory(device_, &mem_alloc, NULL, &depth.mem); - assert(res == VK_SUCCESS); - - res = vkBindImageMemory(device_, depth.image, depth.mem, 0); - assert(res == VK_SUCCESS); - - TransitionImageLayout2(cmd, depth.image, - aspectMask, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); - - VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - depth_view_info.image = depth.image; - depth_view_info.format = depth_format; - depth_view_info.components.r = VK_COMPONENT_SWIZZLE_R; - depth_view_info.components.g = VK_COMPONENT_SWIZZLE_G; - depth_view_info.components.b = VK_COMPONENT_SWIZZLE_B; - depth_view_info.components.a = VK_COMPONENT_SWIZZLE_A; - depth_view_info.subresourceRange.aspectMask = aspectMask; - depth_view_info.subresourceRange.baseMipLevel = 0; - depth_view_info.subresourceRange.levelCount = 1; - depth_view_info.subresourceRange.baseArrayLayer = 0; - depth_view_info.subresourceRange.layerCount = 1; - depth_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - depth_view_info.flags = 0; - - res = vkCreateImageView(device_, &depth_view_info, NULL, &depth.view); - assert(res == VK_SUCCESS); -} - #ifdef _WIN32 void VulkanContext::InitSurfaceWin32(HINSTANCE conn, HWND wnd) { connection = conn; @@ -855,39 +624,32 @@ void VulkanContext::InitQueue() { // supported format will be returned. if (formatCount == 0 || (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)) { ILOG("swapchain_format: Falling back to B8G8R8A8_UNORM"); - swapchain_format = VK_FORMAT_B8G8R8A8_UNORM; + swapchainFormat_ = VK_FORMAT_B8G8R8A8_UNORM; } else { - swapchain_format = VK_FORMAT_UNDEFINED; + swapchainFormat_ = VK_FORMAT_UNDEFINED; for (uint32_t i = 0; i < formatCount; ++i) { if (surfFormats[i].colorSpace != VK_COLORSPACE_SRGB_NONLINEAR_KHR) { continue; } if (surfFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM || surfFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) { - swapchain_format = surfFormats[i].format; + swapchainFormat_ = surfFormats[i].format; break; } } - if (swapchain_format == VK_FORMAT_UNDEFINED) { + if (swapchainFormat_ == VK_FORMAT_UNDEFINED) { // Okay, take the first one then. - swapchain_format = surfFormats[0].format; + swapchainFormat_ = surfFormats[0].format; } - ILOG("swapchain_format: %d (/%d)", swapchain_format, formatCount); + ILOG("swapchain_format: %d (/%d)", swapchainFormat_, formatCount); } delete[] surfFormats; vkGetDeviceQueue(device_, graphics_queue_family_index_, 0, &gfx_queue_); ILOG("gfx_queue_: %p", gfx_queue_); - - VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - semaphoreCreateInfo.flags = 0; - res = vkCreateSemaphore(device_, &semaphoreCreateInfo, nullptr, &acquireSemaphore_); - assert(res == VK_SUCCESS); - res = vkCreateSemaphore(device_, &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore_); - assert(res == VK_SUCCESS); } -bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { +bool VulkanContext::InitSwapchain() { VkResult U_ASSERT_ONLY res; VkSurfaceCapabilitiesKHR surfCapabilities; @@ -967,7 +729,7 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { VkSwapchainCreateInfoKHR swap_chain_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; swap_chain_info.surface = surface_; swap_chain_info.minImageCount = desiredNumberOfSwapChainImages; - swap_chain_info.imageFormat = swapchain_format; + swap_chain_info.imageFormat = swapchainFormat_; swap_chain_info.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; swap_chain_info.imageExtent.width = swapChainExtent.width; swap_chain_info.imageExtent.height = swapChainExtent.height; @@ -988,141 +750,15 @@ bool VulkanContext::InitSwapchain(VkCommandBuffer cmd) { swap_chain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; } - res = vkCreateSwapchainKHR(device_, &swap_chain_info, NULL, &swap_chain_); + res = vkCreateSwapchainKHR(device_, &swap_chain_info, NULL, &swapchain_); assert(res == VK_SUCCESS); if (res != VK_SUCCESS) { return false; } - res = vkGetSwapchainImagesKHR(device_, swap_chain_, &swapchainImageCount, nullptr); - assert(res == VK_SUCCESS); - - ILOG("Vulkan swapchain image count: %d", swapchainImageCount); - - VkImage* swapchainImages = new VkImage[swapchainImageCount]; - assert(swapchainImages); - res = vkGetSwapchainImagesKHR(device_, swap_chain_, &swapchainImageCount, swapchainImages); - assert(res == VK_SUCCESS); - - for (uint32_t i = 0; i < swapchainImageCount; i++) { - swap_chain_buffer sc_buffer; - - VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - color_image_view.format = swapchain_format; - color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; - color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; - color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; - color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; - color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - color_image_view.subresourceRange.baseMipLevel = 0; - color_image_view.subresourceRange.levelCount = 1; - color_image_view.subresourceRange.baseArrayLayer = 0; - color_image_view.subresourceRange.layerCount = 1; - color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; - color_image_view.flags = 0; - - sc_buffer.image = swapchainImages[i]; - - // TODO: Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring - // in image to render to will be to transition them away from that. - TransitionImageLayout2(cmd, sc_buffer.image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - - color_image_view.image = sc_buffer.image; - - res = vkCreateImageView(device_, &color_image_view, NULL, &sc_buffer.view); - swapChainBuffers.push_back(sc_buffer); - assert(res == VK_SUCCESS); - } - - delete[] swapchainImages; - current_buffer = 0; return true; } -void VulkanContext::InitSurfaceRenderPass(bool include_depth, bool clear) { - // Need attachments for render target and depth buffer - VkAttachmentDescription attachments[2]{}; - attachments[0].format = swapchain_format; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].flags = 0; - - if (include_depth) { - attachments[1].format = depth.format; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].flags = 0; - } - - VkAttachmentReference color_reference{}; - color_reference.attachment = 0; - color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depth_reference{}; - depth_reference.attachment = 1; - depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.flags = 0; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = nullptr; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_reference; - subpass.pResolveAttachments = nullptr; - subpass.pDepthStencilAttachment = include_depth ? &depth_reference : nullptr; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - - VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rp_info.attachmentCount = include_depth ? 2 : 1; - rp_info.pAttachments = attachments; - rp_info.subpassCount = 1; - rp_info.pSubpasses = &subpass; - rp_info.dependencyCount = 0; - rp_info.pDependencies = nullptr; - - VkResult U_ASSERT_ONLY res = vkCreateRenderPass(device_, &rp_info, nullptr, &surface_render_pass_); - assert(res == VK_SUCCESS); -} - -void VulkanContext::InitFramebuffers(bool include_depth) { - VkResult U_ASSERT_ONLY res; - VkImageView attachments[2]; - attachments[1] = depth.view; - - ILOG("InitFramebuffers: %dx%d", width_, height_); - VkFramebufferCreateInfo fb_info{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - fb_info.renderPass = surface_render_pass_; - fb_info.attachmentCount = include_depth ? 2 : 1; - fb_info.pAttachments = attachments; - fb_info.width = width_; - fb_info.height = height_; - fb_info.layers = 1; - - framebuffers_.resize(swapchainImageCount); - - for (uint32_t i = 0; i < swapchainImageCount; i++) { - attachments[0] = swapChainBuffers[i].view; - res = vkCreateFramebuffer(device_, &fb_info, nullptr, &framebuffers_[i]); - assert(res == VK_SUCCESS); - } -} - VkFence VulkanContext::CreateFence(bool presignalled) { VkFence fence; VkFenceCreateInfo fenceInfo{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; @@ -1131,49 +767,6 @@ VkFence VulkanContext::CreateFence(bool presignalled) { return fence; } -void VulkanContext::WaitAndResetFence(VkFence fence) { - vkWaitForFences(device_, 1, &fence, true, UINT64_MAX); - vkResetFences(device_, 1, &fence); -} - -void VulkanContext::DestroyDepthStencilBuffer() { - if (depth.view != VK_NULL_HANDLE) - vkDestroyImageView(device_, depth.view, nullptr); - if (depth.image != VK_NULL_HANDLE) - vkDestroyImage(device_, depth.image, nullptr); - if (depth.mem != VK_NULL_HANDLE) - vkFreeMemory(device_, depth.mem, nullptr); - - depth.view = VK_NULL_HANDLE; - depth.image = VK_NULL_HANDLE; - depth.mem = VK_NULL_HANDLE; -} - -void VulkanContext::DestroySwapChain() { - for (uint32_t i = 0; i < swapchainImageCount; i++) { - vkDestroyImageView(device_, swapChainBuffers[i].view, nullptr); - } - if (swap_chain_ != VK_NULL_HANDLE) - vkDestroySwapchainKHR(device_, swap_chain_, nullptr); - swap_chain_ = VK_NULL_HANDLE; - swapChainBuffers.clear(); - vkDestroySemaphore(device_, acquireSemaphore_, nullptr); - vkDestroySemaphore(device_, renderingCompleteSemaphore_, nullptr); -} - -void VulkanContext::DestroyFramebuffers() { - for (uint32_t i = 0; i < framebuffers_.size(); i++) { - vkDestroyFramebuffer(device_, framebuffers_[i], nullptr); - } - framebuffers_.clear(); -} - -void VulkanContext::DestroySurfaceRenderPass() { - if (surface_render_pass_ != VK_NULL_HANDLE) - vkDestroyRenderPass(device_, surface_render_pass_, nullptr); - surface_render_pass_ = VK_NULL_HANDLE; -} - void VulkanContext::DestroyDevice() { vkDestroyDevice(device_, nullptr); device_ = nullptr; diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 925240b5c7..e8204dba4c 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -195,8 +195,7 @@ private: std::vector callbacks_; }; -// VulkanContext sets up the basics necessary for rendering to a window, including framebuffers etc. -// Optionally, it can create a depth buffer for you as well. +// VulkanContext manages the device and swapchain, and deferred deletion of objects. class VulkanContext { public: VulkanContext(); @@ -227,19 +226,12 @@ public: void ReinitSurfaceAndroid(int width, int height); #endif void InitQueue(); - bool InitObjects(bool depthPresent); - bool InitSwapchain(VkCommandBuffer cmd); - void InitSurfaceRenderPass(bool include_depth, bool clear); - void InitFramebuffers(bool include_depth); - void InitDepthStencilBuffer(VkCommandBuffer cmd); + bool InitObjects(); + bool InitSwapchain(); // Also destroys the surface. void DestroyObjects(); - void DestroySurfaceRenderPass(); - void DestroyFramebuffers(); - void DestroySwapChain(); - void DestroyDepthStencilBuffer(); void DestroyDevice(); void WaitUntilQueueIdle(); @@ -248,41 +240,17 @@ public: VkFence CreateFence(bool presignalled); bool CreateShaderModule(const std::vector &spirv, VkShaderModule *shaderModule); - void WaitAndResetFence(VkFence fence); + int GetBackbufferWidth() { return width_; } + int GetBackbufferHeight() { return height_; } - int GetWidth() { return width_; } - int GetHeight() { return height_; } - - VkCommandBuffer GetInitCommandBuffer(); - - VkFramebuffer GetSurfaceFramebuffer() { - return framebuffers_[current_buffer]; - } - // This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass. - VkCommandBuffer GetSurfaceCommandBuffer() { - return frame_[curFrame_].cmdBuf; - } - - VkCommandBuffer BeginFrame(); - // The surface render pass is special because it has to acquire the backbuffer, and may thus "block". - // Use the returned command buffer to enqueue commands that render to the backbuffer. - // To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd). - VkCommandBuffer BeginSurfaceRenderPass(VkClearValue clear_values[2]); - // May eventually need the ability to break and resume the backbuffer render pass in a few rare cases. - void EndSurfaceRenderPass(); + void BeginFrame(); void EndFrame(); - void QueueBeforeSurfaceRender(VkCommandBuffer cmd); - bool MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); VkResult InitDebugMsgCallback(PFN_vkDebugReportCallbackEXT dbgFunc, int bits, void *userdata = nullptr); void DestroyDebugMsgCallback(); - VkRenderPass GetSurfaceRenderPass() const { - return surface_render_pass_; - } - VkPhysicalDevice GetPhysicalDevice(int n = 0) const { return physical_devices_[n]; } @@ -327,8 +295,19 @@ public: return inflightFrames_; } + int GetCurFrame() const { + return curFrame_; + } + + VkSwapchainKHR GetSwapchain() const { + return swapchain_; + } + VkFormat GetSwapchainFormat() const { + return swapchainFormat_; + } + enum { - MAX_INFLIGHT_FRAMES = 2, + MAX_INFLIGHT_FRAMES = 3, }; private: @@ -340,9 +319,6 @@ private: bool CheckLayers(const std::vector &layer_props, const std::vector &layer_names) const; - VkSemaphore acquireSemaphore_; - VkSemaphore renderingCompleteSemaphore_; - #ifdef _WIN32 HINSTANCE connection = nullptr; // hInstance - Windows Instance HWND window = nullptr; // hWnd - window handle @@ -380,36 +356,15 @@ private: // Custom collection of things that are good to know VulkanPhysicalDeviceInfo deviceInfo_; - struct swap_chain_buffer { - VkImage image; - VkImageView view; - }; - // Swap chain int width_ = 0; int height_ = 0; int flags_ = 0; - VkFormat swapchain_format = VK_FORMAT_UNDEFINED; - std::vector framebuffers_; - uint32_t swapchainImageCount = 0; - VkSwapchainKHR swap_chain_ = VK_NULL_HANDLE; - std::vector swapChainBuffers; int inflightFrames_ = MAX_INFLIGHT_FRAMES; - // Manages flipping command buffers for the backbuffer render pass. - // It is recommended to do the same for other rendering passes. struct FrameData { - FrameData() : hasInitCommands(false), cmdInit(nullptr), cmdBuf(nullptr) {} - - VkFence fence; - bool hasInitCommands; - - // TODO: Move to frame data - VkCommandPool cmdPool; - VkCommandBuffer cmdInit; - VkCommandBuffer cmdBuf; - + FrameData() {} VulkanDeleteList deleteList; }; FrameData frame_[MAX_INFLIGHT_FRAMES]; @@ -421,15 +376,9 @@ private: std::vector msg_callbacks; - struct { - VkFormat format; - VkImage image = VK_NULL_HANDLE; - VkDeviceMemory mem = VK_NULL_HANDLE; - VkImageView view = VK_NULL_HANDLE; - } depth; + VkSwapchainKHR swapchain_; + VkFormat swapchainFormat_; - VkRenderPass surface_render_pass_ = VK_NULL_HANDLE; - uint32_t current_buffer = 0; uint32_t queue_count = 0; VkPhysicalDeviceFeatures featuresAvailable_; @@ -438,15 +387,15 @@ private: std::vector cmdQueue_; }; -// Stand-alone utility functions -void VulkanBeginCommandBuffer(VkCommandBuffer cmd); - // Detailed control. void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); +void TransitionFromPresent(VkCommandBuffer cmd, VkImage image); +void TransitionToPresent(VkCommandBuffer cmd, VkImage image); + // GLSL compiler void init_glslang(); void finalize_glslang(); diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp index 19291746ae..41cf32c824 100644 --- a/Common/Vulkan/VulkanImage.cpp +++ b/Common/Vulkan/VulkanImage.cpp @@ -84,11 +84,9 @@ uint8_t *VulkanTexture::Lock(int level, int *rowPitch) { return (uint8_t *)data; } -void VulkanTexture::Unlock() { +void VulkanTexture::Unlock(VkCommandBuffer cmd) { vkUnmapMemory(vulkan_->GetDevice(), mappableMemory); - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - // if we already have an image, queue it for destruction and forget it. Wipe(); @@ -227,11 +225,9 @@ static bool IsDepthStencilFormat(VkFormat format) { } } -bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { +bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { Wipe(); - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); - tex_width = w; tex_height = h; numMips_ = numMips; @@ -336,7 +332,7 @@ bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkI return true; } -void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) { +void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) { VkBufferImageCopy copy_region = {}; copy_region.bufferOffset = offset; copy_region.bufferRowLength = (uint32_t)rowLength; @@ -349,12 +345,10 @@ void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buf copy_region.imageSubresource.baseArrayLayer = 0; copy_region.imageSubresource.layerCount = 1; - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); } -void VulkanTexture::EndCreate() { - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); +void VulkanTexture::EndCreate(VkCommandBuffer cmd) { TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, @@ -362,8 +356,7 @@ void VulkanTexture::EndCreate() { VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); } -void VulkanTexture::TransitionForUpload() { - VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer(); +void VulkanTexture::TransitionForUpload(VkCommandBuffer cmd) { TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, diff --git a/Common/Vulkan/VulkanImage.h b/Common/Vulkan/VulkanImage.h index 50b9b0b651..51ffbe5e22 100644 --- a/Common/Vulkan/VulkanImage.h +++ b/Common/Vulkan/VulkanImage.h @@ -26,16 +26,16 @@ public: // been called. VkResult Create(int w, int h, VkFormat format); uint8_t *Lock(int level, int *rowPitch); - void Unlock(); + void Unlock(VkCommandBuffer cmd); // Fast uploads from buffer. Mipmaps supported. // Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip. // When using UploadMip, initialLayout should be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. - bool CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr); - void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels - void EndCreate(); + bool CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr); + void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels + void EndCreate(VkCommandBuffer cmd); - void TransitionForUpload(); + void TransitionForUpload(VkCommandBuffer cmd); int GetNumMips() const { return numMips_; } void Destroy(); diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp index e097d44eca..734ae34ac8 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -1,116 +1,1079 @@ #include "base/logging.h" #include "VulkanRenderManager.h" +#include "VulkanContext.h" + +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { + VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + ici.arrayLayers = 1; + ici.mipLevels = 1; + ici.extent.width = width; + ici.extent.height = height; + ici.extent.depth = 1; + ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + ici.imageType = VK_IMAGE_TYPE_2D; + ici.samples = VK_SAMPLE_COUNT_1_BIT; + ici.tiling = VK_IMAGE_TILING_OPTIMAL; + ici.format = format; + ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (color) { + ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } else { + ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image); + + // TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers + + VkMemoryRequirements memreq; + vkGetImageMemoryRequirements(vulkan->GetDevice(), img.image, &memreq); + + VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + alloc.allocationSize = memreq.size; + vulkan->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex); + VkResult res = vkAllocateMemory(vulkan->GetDevice(), &alloc, nullptr, &img.memory); + assert(res == VK_SUCCESS); + res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0); + assert(res == VK_SUCCESS); + + VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + + VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + ivci.format = ici.format; + ivci.image = img.image; + ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; + ivci.subresourceRange.aspectMask = aspects; + ivci.subresourceRange.layerCount = 1; + ivci.subresourceRange.levelCount = 1; + res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.imageView); + assert(res == VK_SUCCESS); + + VkPipelineStageFlagBits dstStage; + VkAccessFlagBits dstAccessMask; + switch (initialLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + break; + } + + TransitionImageLayout2(cmd, img.image, aspects, + VK_IMAGE_LAYOUT_UNDEFINED, initialLayout, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, dstStage, + 0, dstAccessMask); + img.layout = initialLayout; +} + +VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) { + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; + cmd_pool_info.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex(); + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPool); + assert(res == VK_SUCCESS); + + VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + cmd_alloc.commandPool = frameData_[i].cmdPool; + cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_alloc.commandBufferCount = 2; + + VkCommandBuffer cmdBuf[2]; + res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, cmdBuf); + assert(res == VK_SUCCESS); + frameData_[i].mainCmd = cmdBuf[0]; + frameData_[i].initCmd = cmdBuf[1]; + frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on + } + + VkSwapchainKHR swapChain = vulkan_->GetSwapchain(); + VkResult res = vkGetSwapchainImagesKHR(vulkan->GetDevice(), swapChain, &swapchainImageCount, nullptr); + assert(res == VK_SUCCESS); + + ILOG("Vulkan swapchain image count: %d", swapchainImageCount); + + VkImage* swapchainImages = new VkImage[swapchainImageCount]; + assert(swapchainImages); + res = vkGetSwapchainImagesKHR(vulkan->GetDevice(), swapChain, &swapchainImageCount, swapchainImages); + assert(res == VK_SUCCESS); + + VkCommandBuffer cmdInit = frameData_[0].initCmd; + frameData_[0].hasInitCommands = true; + + for (uint32_t i = 0; i < swapchainImageCount; i++) { + SwapchainImageData sc_buffer; + + VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + color_image_view.format = vulkan_->GetSwapchainFormat(); + color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; + color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; + color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; + color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; + color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + color_image_view.subresourceRange.baseMipLevel = 0; + color_image_view.subresourceRange.levelCount = 1; + color_image_view.subresourceRange.baseArrayLayer = 0; + color_image_view.subresourceRange.layerCount = 1; + color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; + color_image_view.flags = 0; + + sc_buffer.image = swapchainImages[i]; + + // Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring + // in image to render to will be to transition them away from that. + TransitionImageLayout2(cmdInit, sc_buffer.image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + + color_image_view.image = sc_buffer.image; + + res = vkCreateImageView(vulkan_->GetDevice(), + &color_image_view, NULL, &sc_buffer.view); + swapchainImages_.push_back(sc_buffer); + assert(res == VK_SUCCESS); + } + delete[] swapchainImages; + current_buffer = 0; + + VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + semaphoreCreateInfo.flags = 0; + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); + assert(res == VK_SUCCESS); + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore); + assert(res == VK_SUCCESS); + + InitDepthStencilBuffer(cmdInit); + InitSurfaceRenderPass(); +} + +VulkanRenderManager::~VulkanRenderManager() { + VkDevice device = vulkan_->GetDevice(); + for (uint32_t i = 0; i < swapchainImageCount; i++) { + vkDestroyImageView(device, swapchainImages_[i].view, nullptr); + } + swapchainImages_.clear(); + vkDestroySemaphore(device, acquireSemaphore_, nullptr); + vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr); + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; + vkFreeCommandBuffers(device, frameData_[i].cmdPool, 2, cmdBuf); + vkDestroyCommandPool(device, frameData_[i].cmdPool, nullptr); + vkDestroyFence(device, frameData_[i].fence, nullptr); + } + if (backbufferRenderPass_ != VK_NULL_HANDLE) + vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); + for (uint32_t i = 0; i < framebuffers_.size(); i++) { + vkDestroyFramebuffer(device, framebuffers_[i], nullptr); + } + framebuffers_.clear(); + for (int i = 0; i < 9; i++) { + vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); + } +} + +void VulkanRenderManager::ThreadFunc() { + while (true) { + std::unique_lock lock(mutex_); + condVar_.wait(lock); + if (steps_.size()) { + stepsOnThread_ = std::move(steps_); + } + // ... + } +} void VulkanRenderManager::BeginFrameWrites() { vulkan_->BeginFrame(); - renderPasses_.push_back(new VKRRenderPass); - curRp_ = renderPasses_.back(); + + VkDevice device = vulkan_->GetDevice(); + + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + + // Make sure the very last command buffer from the frame before the previous has been fully executed. + vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); + vkResetFences(device, 1, &frameData.fence); + + // Reset both command buffers in one fell swoop. + // Note that on the first frame, there might already be commands so don't reset in that case. + if (!frameData.hasInitCommands) { + vkResetCommandPool(vulkan_->GetDevice(), frameData.cmdPool, 0); + } + + VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + begin.pInheritanceInfo = nullptr; + VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); +} + +VkCommandBuffer VulkanRenderManager::GetInitCmd() { + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + if (!frameData_->hasInitCommands) { + VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + begin.pInheritanceInfo = nullptr; + VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + } + return frameData_[vulkan_->GetCurFrame()].initCmd; } void VulkanRenderManager::EndFrame() { + FrameData &frame = frameData_[vulkan_->GetCurFrame()]; + TransitionToPresent(frame.mainCmd, swapchainImages_[current_buffer].image); + + VkResult res = vkEndCommandBuffer(frame.mainCmd); + assert(res == VK_SUCCESS); + + // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. + // This way we bunch up all the initialization needed for the frame, we render to + // other buffers before the back buffer, and then last we render to the backbuffer. + + int numCmdBufs = 0; + std::vector cmdBufs; + if (frame.hasInitCommands) { + vkEndCommandBuffer(frame.initCmd); + cmdBufs.push_back(frame.initCmd); + frame.hasInitCommands = false; + } + + cmdBufs.push_back(frame.mainCmd); + + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &acquireSemaphore_; + VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; + submit_info.pWaitDstStageMask = waitStage; + submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); + submit_info.pCommandBuffers = cmdBufs.data(); + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &renderingCompleteSemaphore; + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frame.fence); + assert(res == VK_SUCCESS); + + VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; + present.swapchainCount = 1; + VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); + present.pSwapchains = &swapchain; + present.pImageIndices = ¤t_buffer; + present.pWaitSemaphores = &renderingCompleteSemaphore; + present.waitSemaphoreCount = 1; + present.pResults = nullptr; + + res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); + // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI + // return codes + assert(res == VK_SUCCESS); + vulkan_->EndFrame(); } +void VulkanRenderManager::Sync() { + +} + +void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb) { + // This is what queues up new passes, and can end previous ones. +} + + +void VulkanRenderManager::BeginSurfaceRenderPass(VkCommandBuffer cmd, VkClearValue clear_value) { + FrameData &frame = frameData_[vulkan_->GetCurFrame()]; + VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + rp_begin.renderPass = backbufferRenderPass_; + rp_begin.framebuffer = framebuffers_[current_buffer]; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = curWidth_; + rp_begin.renderArea.extent.height = curHeight_; + rp_begin.clearValueCount = 1; + rp_begin.pClearValues = &clear_value; + vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); +} + +void VulkanRenderManager::EndSurfaceRenderPass(VkCommandBuffer cmd) { + // ILOG("VulkanContext::EndSurfaceRenderPass"); + vkCmdEndRenderPass(cmd); +} + +void VulkanRenderManager::InitFramebuffers() { + VkResult U_ASSERT_ONLY res; + VkImageView attachments[1]; + + ILOG("InitFramebuffers: %dx%d", curWidth_, curHeight_); + VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + fb_info.renderPass = backbufferRenderPass_; + fb_info.attachmentCount = 1; + fb_info.pAttachments = attachments; + fb_info.width = curWidth_; + fb_info.height = curHeight_; + fb_info.layers = 1; + + framebuffers_.resize(swapchainImageCount); + + for (uint32_t i = 0; i < swapchainImageCount; i++) { + attachments[0] = swapchainImages_[i].view; + res = vkCreateFramebuffer(vulkan_->GetDevice(), &fb_info, nullptr, &framebuffers_[i]); + assert(res == VK_SUCCESS); + } +} + +void VulkanRenderManager::InitSurfaceRenderPass() { + VkResult U_ASSERT_ONLY res; + + VkAttachmentDescription attachments[2]; + attachments[0].format = vulkan_->GetSwapchainFormat(); + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].flags = 0; + + attachments[1].format = depth_.format; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].flags = 0; + + VkAttachmentReference color_reference = {}; + color_reference.attachment = 0; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depth_reference{}; + depth_reference.attachment = 1; + depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = nullptr; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + subpass.pResolveAttachments = nullptr; + subpass.pDepthStencilAttachment = &depth_reference; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = nullptr; + + VkRenderPassCreateInfo rp_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + rp_info.pNext = nullptr; + rp_info.attachmentCount = 2; + rp_info.pAttachments = attachments; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 0; + rp_info.pDependencies = nullptr; + + res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, NULL, &backbufferRenderPass_); + assert(res == VK_SUCCESS); +} + +void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { + VkResult U_ASSERT_ONLY res; + bool U_ASSERT_ONLY pass; + + const VkFormat depth_format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; + int aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = depth_format; + image_info.extent.width = curWidth_; + image_info.extent.height = curHeight_; + image_info.extent.depth = 1; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.queueFamilyIndexCount = 0; + image_info.pQueueFamilyIndices = nullptr; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + image_info.flags = 0; + + VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkMemoryRequirements mem_reqs; + + depth_.format = depth_format; + + VkDevice device = vulkan_->GetDevice(); + res = vkCreateImage(device, &image_info, NULL, &depth_.image); + assert(res == VK_SUCCESS); + + vkGetImageMemoryRequirements(device, depth_.image, &mem_reqs); + + mem_alloc.allocationSize = mem_reqs.size; + // Use the memory properties to determine the type of memory required + pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, + 0, /* No requirements */ + &mem_alloc.memoryTypeIndex); + assert(pass); + + res = vkAllocateMemory(device, &mem_alloc, NULL, &depth_.mem); + assert(res == VK_SUCCESS); + + res = vkBindImageMemory(device, depth_.image, depth_.mem, 0); + assert(res == VK_SUCCESS); + + TransitionImageLayout2(cmd, depth_.image, + aspectMask, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + + VkImageViewCreateInfo depth_view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + depth_view_info.image = depth_.image; + depth_view_info.format = depth_format; + depth_view_info.components.r = VK_COMPONENT_SWIZZLE_R; + depth_view_info.components.g = VK_COMPONENT_SWIZZLE_G; + depth_view_info.components.b = VK_COMPONENT_SWIZZLE_B; + depth_view_info.components.a = VK_COMPONENT_SWIZZLE_A; + depth_view_info.subresourceRange.aspectMask = aspectMask; + depth_view_info.subresourceRange.baseMipLevel = 0; + depth_view_info.subresourceRange.levelCount = 1; + depth_view_info.subresourceRange.baseArrayLayer = 0; + depth_view_info.subresourceRange.layerCount = 1; + depth_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + depth_view_info.flags = 0; + + res = vkCreateImageView(device, &depth_view_info, NULL, &depth_.view); + assert(res == VK_SUCCESS); +} + +void VulkanRenderManager::InitRenderpasses() { + // Create a bunch of render pass objects, for normal rendering with a depth buffer, + // with clearing, without clearing, and dont-care for both depth/stencil and color, so 3*3=9 combos. + VkAttachmentDescription attachments[2] = {}; + attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].flags = 0; + + attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].flags = 0; + + VkAttachmentReference color_reference = {}; + color_reference.attachment = 0; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depth_reference = {}; + depth_reference.attachment = 1; + depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = nullptr; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + subpass.pResolveAttachments = nullptr; + subpass.pDepthStencilAttachment = &depth_reference; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = nullptr; + + VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + rp.attachmentCount = 2; + rp.pAttachments = attachments; + rp.subpassCount = 1; + rp.pSubpasses = &subpass; + rp.dependencyCount = 0; + rp.pDependencies = nullptr; + + for (int depth = 0; depth < 3; depth++) { + switch ((RenderPassAction)depth) { + case RenderPassAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case RenderPassAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case RenderPassAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + } + for (int color = 0; color < 3; color++) { + switch ((RenderPassAction)color) { + case RenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case RenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case RenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + } + vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((RenderPassAction)color, (RenderPassAction)depth)]); + } + } +} + +void VulkanRenderManager::BeginRenderPass() { + std::unique_lock lock(rpLock_); + + VKRStep *pass = new VKRStep(VKStepType::RENDER); + pass->stepType = VKStepType::RENDER; + steps_.push_back(pass); + curStep_ = steps_.back(); +} void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); // If this is the first drawing command, merge it into the pass. - if (curRp_->numDraws == 0) { - curRp_->clearColor = clearColor; - curRp_->clearZ = clearZ; - curRp_->clearStencil = clearStencil; - curRp_->clearMask = clearMask; + if (curStep_->render.numDraws == 0) { + curStep_->render.clearColor = clearColor; + curStep_->render.clearDepth = clearZ; + curStep_->render.clearStencil = clearStencil; + curStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? RenderPassAction::CLEAR : RenderPassAction::KEEP; + curStep_->render.depthStencil = (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) ? RenderPassAction::CLEAR : RenderPassAction::KEEP; } else { - VkRenderData data{ VKR_CLEAR }; + VkRenderData data{ VkRenderCmd::CLEAR }; data.clear.clearColor = clearColor; data.clear.clearZ = clearZ; data.clear.clearStencil = clearStencil; data.clear.clearMask = clearMask; - curRp_->commands.push_back(data); + curStep_->commands.push_back(data); } } -void VulkanRenderManager::Flush(VkCommandBuffer cmdbuf) { - // Optimizes renderpasses, then sequences them. - for (int i = 0; i < renderPasses_.size(); i++) { - auto &commands = renderPasses_[i]->commands; - for (const auto &c : commands) { - switch (c.cmd) { - case VKR_VIEWPORT: - vkCmdSetViewport(cmdbuf, 0, 1, &c.viewport.vp); - break; +void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint) { + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + EndCurrentRenderpass(frameData.mainCmd); +} - case VKR_SCISSOR: - vkCmdSetScissor(cmdbuf, 0, 1, &c.scissor.scissor); - break; +void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter) { + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + EndCurrentRenderpass(frameData.mainCmd); +} - case VKR_DRAW_INDEXED: - vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); - vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); - vkCmdBindIndexBuffer(cmdbuf, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); - vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); - vkCmdDrawIndexed(cmdbuf, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); - break; - - case VKR_DRAW: - vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); - vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); - vkCmdBindVertexBuffers(cmdbuf, 0, 1, &c.draw.vbuffer, &c.draw.voffset); - vkCmdDraw(cmdbuf, c.draw.count, 1, 0, 0); - break; - - case VKR_STENCIL: - vkCmdSetStencilWriteMask(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); - vkCmdSetStencilCompareMask(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); - vkCmdSetStencilReference(cmdbuf, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); - break; - - case VKR_BLEND: - vkCmdSetBlendConstants(cmdbuf, c.blendColor.color); - break; - - case VKR_CLEAR: - { - int numAttachments = 0; - VkClearRect rc{}; - rc.baseArrayLayer = 0; - rc.layerCount = 1; - rc.rect.extent.width = curWidth_; - rc.rect.extent.height = curHeight_; - VkClearAttachment attachments[2]; - if (c.clear.clearMask & VK_IMAGE_ASPECT_COLOR_BIT) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - attachment.colorAttachment = 0; - Uint8x4ToFloat4(attachment.clearValue.color.float32, c.clear.clearColor); - } - if (c.clear.clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = 0; - if (c.clear.clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) { - attachment.clearValue.depthStencil.depth = c.clear.clearZ; - attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (c.clear.clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) { - attachment.clearValue.depthStencil.stencil = c.clear.clearStencil; - attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } - if (numAttachments) { - vkCmdClearAttachments(cmdbuf, numAttachments, attachments, 1, &rc); - } - break; - } - default: - ELOG("Unimpl queue command"); - ; - } +VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { + // Should just mark the dependency and return the image. + for (int i = 0; i < (int)steps_.size() - 1; i++) { + if (steps_[i]->stepType == VKStepType::RENDER && steps_[i]->render.framebuffer == fb) { + if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) + steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + else + Crash(); // May need to shadow the framebuffer? } - delete renderPasses_[i]; } - renderPasses_.clear(); + + /* + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; + switch (fb->color.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + } + TransitionImageLayout2(transitionCmdBuf, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + srcAccessMask, VK_ACCESS_SHADER_READ_BIT); + fb->color.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + */ + return fb->color.imageView; } -void VulkanRenderManager::Sync(VkCommandBuffer cmd) { +void VulkanRenderManager::Flush() { + { + std::unique_lock lock(rpLock_); + stepsOnThread_ = std::move(steps_); + } + FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + VkCommandBuffer cmd = frameData.mainCmd; + + VkDevice device = vulkan_->GetDevice(); + + // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. + // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, VK_NULL_HANDLE, ¤t_buffer); + // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR + // return codes + assert(res == VK_SUCCESS); + TransitionFromPresent(frameData.mainCmd, swapchainImages_[current_buffer].image); + + // Optimizes renderpasses, then sequences them. + for (int i = 0; i < stepsOnThread_.size(); i++) { + const VKRStep &step = *stepsOnThread_[i]; + switch (step.stepType) { + case VKStepType::RENDER: + PerformRenderPass(step, cmd); + break; + case VKStepType::COPY: + PerformCopy(step, cmd); + break; + case VKStepType::BLIT: + PerformBlit(step, cmd); + break; + case VKStepType::READBACK: + // PerformReadback + break; + } + delete stepsOnThread_[i]; + } + stepsOnThread_.clear(); +} + +void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { + PerformBindFramebufferAsRenderTarget(step, cmd); + auto &commands = step.commands; + for (const auto &c : commands) { + switch (c.cmd) { + case VkRenderCmd::VIEWPORT: + vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp); + break; + + case VkRenderCmd::SCISSOR: + vkCmdSetScissor(cmd, 0, 1, &c.scissor.scissor); + break; + + case VkRenderCmd::BLEND: + vkCmdSetBlendConstants(cmd, c.blendColor.color); + break; + + case VkRenderCmd::STENCIL: + vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); + vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); + vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); + break; + + case VkRenderCmd::DRAW_INDEXED: + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); + vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); + vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); + break; + + case VkRenderCmd::DRAW: + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); + vkCmdDraw(cmd, c.draw.count, 1, 0, 0); + break; + + case VkRenderCmd::CLEAR: + { + int numAttachments = 0; + VkClearRect rc{}; + rc.baseArrayLayer = 0; + rc.layerCount = 1; + rc.rect.extent.width = curWidth_; + rc.rect.extent.height = curHeight_; + VkClearAttachment attachments[2]; + if (c.clear.clearMask & VK_IMAGE_ASPECT_COLOR_BIT) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + attachment.colorAttachment = 0; + Uint8x4ToFloat4(attachment.clearValue.color.float32, c.clear.clearColor); + } + if (c.clear.clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = 0; + if (c.clear.clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + attachment.clearValue.depthStencil.depth = c.clear.clearZ; + attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (c.clear.clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + attachment.clearValue.depthStencil.stencil = c.clear.clearStencil; + attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } + if (numAttachments) { + vkCmdClearAttachments(cmd, numAttachments, attachments, 1, &rc); + } + break; + } + default: + ELOG("Unimpl queue command"); + ; + } + } + vkCmdEndRenderPass(cmd); +} + +void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { + VkFramebuffer framebuf; + int w; + int h; + VkImageLayout prevLayout; + if (step.render.framebuffer) { + VKRFramebuffer *fb = step.render.framebuffer; + framebuf = fb->framebuf; + w = fb->width; + h = fb->height; + prevLayout = fb->color.layout; + } else { + framebuf = framebuffers_[current_buffer]; + w = vulkan_->GetBackbufferWidth(); + h = vulkan_->GetBackbufferHeight(); + } + + if (framebuf == curFramebuffer_) { + if (framebuf == 0) + Crash(); + if (!curRenderPass_) + Crash(); + + // If we're asking to clear, but already bound, we'll just keep it bound but send a clear command. + // We will try to avoid this as much as possible. + VkClearAttachment clear[2]{}; + int count = 0; + if (step.render.color == RenderPassAction::CLEAR) { + clear[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + Uint8x4ToFloat4(clear[count].clearValue.color.float32, step.render.clearColor); + clear[count].colorAttachment = 0; + count++; + } + + if (step.render.depthStencil == RenderPassAction::CLEAR) { + clear[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + clear[count].clearValue.depthStencil.depth = step.render.clearDepth; + clear[count].clearValue.depthStencil.stencil = step.render.clearStencil; + clear[count].colorAttachment = 0; + count++; + } + + if (count > 0) { + VkClearRect rc{ { 0,0,(uint32_t)w,(uint32_t)h }, 0, 1 }; + vkCmdClearAttachments(cmd, count, clear, 1, &rc); + } + // We're done. + return; + } + + // OK, we're switching framebuffers. + if (curRenderPass_) { + vkCmdEndRenderPass(cmd); + curRenderPass_ = VK_NULL_HANDLE; + curFramebuffer_ = VK_NULL_HANDLE; + } + + VkRenderPass renderPass; + int numClearVals = 0; + VkClearValue clearVal[2]; + memset(clearVal, 0, sizeof(clearVal)); + if (step.render.framebuffer) { + VKRFramebuffer *fb = step.render.framebuffer; + // Now, if the image needs transitioning, let's transition. + // The backbuffer does not, that's handled by VulkanContext. + if (step.render.framebuffer->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = fb->color.image; + barrier.srcAccessMask = 0; + switch (fb->color.layout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + break; + } + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + // TODO: Double-check these flags. Should be fine. + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + fb->color.layout = barrier.newLayout; + } + if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = fb->depth.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = fb->depth.image; + barrier.srcAccessMask = 0; + switch (fb->depth.layout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + } + barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + // TODO: Double-check these flags. Should be fine. + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + fb->depth.layout = barrier.newLayout; + } + + renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; + // ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); + if (step.render.color == RenderPassAction::CLEAR) { + Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); + numClearVals = 1; + } + if (step.render.depthStencil == RenderPassAction::CLEAR) { + clearVal[1].depthStencil.depth = step.render.clearDepth; + clearVal[1].depthStencil.stencil = step.render.clearStencil; + numClearVals = 2; + } + } else { + renderPass = GetSurfaceRenderPass(); + numClearVals = 2; + } + + VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + rp_begin.renderPass = renderPass; + rp_begin.framebuffer = framebuf; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = w; + rp_begin.renderArea.extent.height = h; + rp_begin.clearValueCount = numClearVals; + rp_begin.pClearValues = numClearVals ? clearVal : nullptr; + vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); + curFramebuffer_ = framebuf; + curRenderPass_ = renderPass; + curWidth_ = w; + curHeight_ = h; +} + +void VulkanRenderManager::EndCurrentRenderpass(VkCommandBuffer cmd) { + if (curRenderPass_) { + vkCmdEndRenderPass(cmd); + curRenderPass_ = nullptr; + } +} + +void VulkanRenderManager::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { + VKRFramebuffer *src = step.copy.src; + VKRFramebuffer *dst = step.copy.dst; + + VkImageCopy copy{}; + copy.srcOffset.x = step.copy.srcRect.offset.x; + copy.srcOffset.y = step.copy.srcRect.offset.y; + copy.srcOffset.z = 0; + copy.srcSubresource.mipLevel = 0; + copy.srcSubresource.layerCount = 1; + copy.dstOffset.x = step.copy.dstPos.x; + copy.dstOffset.y = step.copy.dstPos.y; + copy.dstOffset.z = 0; + copy.dstSubresource.mipLevel = 0; + copy.dstSubresource.layerCount = 1; + copy.extent.width = step.copy.srcRect.extent.width; + copy.extent.height = step.copy.srcRect.extent.height; + copy.extent.depth = 1; + + VkImageMemoryBarrier srcBarriers[2]{}; + VkImageMemoryBarrier dstBarriers[2]{}; + int srcCount = 0; + int dstCount = 0; + + // First source barriers. + if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); + } + if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + // We can't copy only depth or only stencil unfortunately. + if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + } + + // TODO: Fix the pipe bits to be bit less conservative. + if (srcCount) { + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + } + if (dstCount) { + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + } + + if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©); + } + if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + copy.srcSubresource.aspectMask = 0; + copy.dstSubresource.aspectMask = 0; + if (step.copy.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (step.copy.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©); + } +} + +void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) { + VkImageMemoryBarrier srcBarriers[2]{}; + VkImageMemoryBarrier dstBarriers[2]{}; + + VKRFramebuffer *src = step.blit.src; + VKRFramebuffer *dst = step.blit.dst; + + int srcCount = 0; + int dstCount = 0; + + VkImageBlit blit{}; + blit.srcOffsets[0].x = step.blit.srcRect.offset.x; + blit.srcOffsets[0].y = step.blit.srcRect.offset.y; + blit.srcOffsets[0].z = 0; + blit.srcOffsets[1].x = step.blit.srcRect.offset.x + step.blit.srcRect.extent.width; + blit.srcOffsets[1].y = step.blit.srcRect.offset.y + step.blit.srcRect.extent.height; + blit.srcOffsets[1].z = 1; + blit.srcSubresource.mipLevel = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0].x = step.blit.dstRect.offset.x; + blit.dstOffsets[0].y = step.blit.dstRect.offset.y; + blit.dstOffsets[0].z = 0; + blit.dstOffsets[1].x = step.blit.dstRect.offset.x + step.blit.dstRect.extent.width; + blit.dstOffsets[1].y = step.blit.dstRect.offset.y + step.blit.dstRect.extent.height; + blit.dstOffsets[1].z = 1; + blit.dstSubresource.mipLevel = 0; + blit.dstSubresource.layerCount = 1; + + // First source barriers. + if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); + } + if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + // We can't copy only depth or only stencil unfortunately. + if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + } + + // TODO: Fix the pipe bits to be bit less conservative. + if (srcCount) { + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + } + if (dstCount) { + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + } + + if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, step.blit.filter); + } + if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + blit.srcSubresource.aspectMask = 0; + blit.dstSubresource.aspectMask = 0; + if (step.blit.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (step.blit.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + vkCmdBlitImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &blit, step.blit.filter); + } +} + +void VulkanRenderManager::SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = img.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = img.image; + barrier.srcAccessMask = 0; + switch (img.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + default: + Crash(); + } + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.subresourceRange.aspectMask = aspect; + img.layout = barrier.newLayout; +} + +void VulkanRenderManager::SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = img.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = img.image; + barrier.srcAccessMask = 0; + switch (img.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + break; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + default: + Crash(); + } + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.subresourceRange.aspectMask = aspect; + img.layout = barrier.newLayout; } diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h index c4c59485eb..fbc8f0976c 100644 --- a/Common/Vulkan/VulkanRenderManager.h +++ b/Common/Vulkan/VulkanRenderManager.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include "Common/Vulkan/VulkanContext.h" #include "math/dataconv.h" @@ -13,14 +15,14 @@ // The cool thing is that you can Flush on a different thread than you record the commands on! -enum VkRenderCmd : uint8_t { - VKR_STENCIL, - VKR_BLEND, - VKR_VIEWPORT, - VKR_SCISSOR, - VKR_CLEAR, - VKR_DRAW, - VKR_DRAW_INDEXED, +enum class VkRenderCmd : uint8_t { + STENCIL, + BLEND, + VIEWPORT, + SCISSOR, + CLEAR, + DRAW, + DRAW_INDEXED, }; struct VkRenderData { @@ -80,47 +82,160 @@ struct VkRenderData { }; }; -struct VKRRenderPass { - VkFramebuffer framebuffer; - uint32_t clearColor; - float clearZ; - int clearStencil; - int clearMask = 0; // VK_IMAGE_ASPECT_COLOR_BIT etc - int dontCareMask = 0; - int numDraws; +enum class VKStepType : uint8_t { + RENDER, + COPY, + BLIT, + READBACK, +}; + +class VKRFramebuffer; + +enum class RenderPassAction { + DONT_CARE, + CLEAR, + KEEP, +}; + +struct VKRStep { + VKRStep(VKStepType _type) : stepType(_type) {} + VKStepType stepType; std::vector commands; + union { + struct { + VKRFramebuffer *framebuffer; + RenderPassAction color; + RenderPassAction depthStencil; + uint32_t clearColor; + float clearDepth; + int clearStencil; + int numDraws; + VkImageLayout finalColorLayout; + } render; + struct { + VKRFramebuffer *src; + VKRFramebuffer *dst; + VkRect2D srcRect; + VkOffset2D dstPos; + int aspectMask; + } copy; + struct { + VKRFramebuffer *src; + VKRFramebuffer *dst; + VkRect2D srcRect; + VkRect2D dstRect; + int aspectMask; + VkFilter filter; + } blit; + struct { + VKRFramebuffer *src; + void *destPtr; + VkRect2D srcRect; + } readback; + }; +}; + +// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine +// to let them have individual non-pooled allocations. Until it's not fine. We'll see. +struct VKImage { + VkImage image; + VkImageView imageView; + VkDeviceMemory memory; + VkImageLayout layout; +}; +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color); + +class VKRFramebuffer { +public: + VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, int _width, int _height) : vulkan_(vk) { + width = _width; + height = _height; + + CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true); + CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false); + + VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + VkImageView views[2]{}; + + fbci.renderPass = renderPass; + fbci.attachmentCount = 2; + fbci.pAttachments = views; + views[0] = color.imageView; + views[1] = depth.imageView; + fbci.width = width; + fbci.height = height; + fbci.layers = 1; + + vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf); + } + ~VKRFramebuffer() { + vulkan_->Delete().QueueDeleteImage(color.image); + vulkan_->Delete().QueueDeleteImage(depth.image); + vulkan_->Delete().QueueDeleteImageView(color.imageView); + vulkan_->Delete().QueueDeleteImageView(depth.imageView); + vulkan_->Delete().QueueDeleteDeviceMemory(color.memory); + vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory); + + vulkan_->Delete().QueueDeleteFramebuffer(framebuf); + } + + int numShadows = 1; // TODO: Support this. + + VkFramebuffer framebuf = VK_NULL_HANDLE; + VKImage color{}; + VKImage depth{}; + int width = 0; + int height = 0; + +private: + VulkanContext *vulkan_; }; class VulkanRenderManager { public: - VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) {} + VulkanRenderManager(VulkanContext *vulkan); + ~VulkanRenderManager(); + + void ThreadFunc(); // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. void BeginFrameWrites(); void EndFrame(); + void BindFramebufferAsRenderTarget(VKRFramebuffer *fb); + VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); + + void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint); + void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter); + + void BeginRenderPass(); + void SetViewport(const VkViewport &vp) { - VkRenderData data{ VKR_VIEWPORT }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::VIEWPORT }; data.viewport.vp = vp; - curRp_->commands.push_back(data); + curStep_->commands.push_back(data); } void SetScissor(const VkRect2D &rc) { - VkRenderData data{ VKR_SCISSOR }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::SCISSOR }; data.scissor.scissor = rc; - curRp_->commands.push_back(data); + curStep_->commands.push_back(data); } void SetBlendFactor(float color[4]) { - VkRenderData data{ VKR_BLEND }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::BLEND }; CopyFloat4(data.blendColor.color, color); - curRp_->commands.push_back(data); + curStep_->commands.push_back(data); } void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { - VkRenderData data{ VKR_DRAW }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::DRAW }; data.draw.count = count; data.draw.pipeline = pipeline; data.draw.pipelineLayout = layout; @@ -130,12 +245,13 @@ public: data.draw.numUboOffsets = numUboOffsets; for (int i = 0; i < numUboOffsets; i++) data.draw.uboOffsets[i] = uboOffsets[i]; - curRp_->commands.push_back(data); - curRp_->numDraws++; + curStep_->commands.push_back(data); + curStep_->render.numDraws++; } void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, VkIndexType indexType) { - VkRenderData data{ VKR_DRAW }; + _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + VkRenderData data{ VkRenderCmd::DRAW_INDEXED }; data.drawIndexed.count = count; data.drawIndexed.pipeline = pipeline; data.drawIndexed.pipelineLayout = layout; @@ -148,21 +264,103 @@ public: for (int i = 0; i < numUboOffsets; i++) data.drawIndexed.uboOffsets[i] = uboOffsets[i]; data.drawIndexed.indexType = indexType; - curRp_->commands.push_back(data); - curRp_->numDraws++; + curStep_->commands.push_back(data); + curStep_->render.numDraws++; } // Can run on a different thread! Just make sure to use BeginFrameWrites. - void Flush(VkCommandBuffer cmd); + void Flush(); // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). - void Sync(VkCommandBuffer cmd); + void Sync(); - std::vector renderPasses_; - VKRRenderPass *curRp_; + std::vector steps_; + std::vector stepsOnThread_; + std::mutex rpLock_; + + VKRStep *curStep_; + + VkCommandBuffer GetInitCmd(); + VkCommandBuffer GetSurfaceCommandBuffer() { + return frameData_[vulkan_->GetCurFrame()].mainCmd; + } + VkRenderPass GetSurfaceRenderPass() const { + return backbufferRenderPass_; + } + VkRenderPass GetRenderPass(int i) const { + return renderPasses_[i]; + } private: + void InitFramebuffers(); + void InitSurfaceRenderPass(); + void InitRenderpasses(); + void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. + + // The surface render pass is special because it has to acquire the backbuffer, and may thus "block". + // Use the returned command buffer to enqueue commands that render to the backbuffer. + // To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd). + void BeginSurfaceRenderPass(VkCommandBuffer cmd, VkClearValue clear_value); + // May eventually need the ability to break and resume the backbuffer render pass in a few rare cases. + void EndSurfaceRenderPass(VkCommandBuffer cmd); + + void EndCurrentRenderpass(VkCommandBuffer cmd); + + void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd); + + void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd); + void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd); + void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); + + inline int RPIndex(RenderPassAction color, RenderPassAction depth) { + return (int)depth * 3 + (int)color; + } + + static void SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + static void SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + + VkSemaphore acquireSemaphore_; + VkSemaphore renderingCompleteSemaphore; + + // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. + // TODO: Create these on demand. + VkRenderPass renderPasses_[9]; + + struct FrameData { + VkFence fence; + VkCommandPool cmdPool; + VkCommandBuffer initCmd; + VkCommandBuffer mainCmd; + bool hasInitCommands; + }; + FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; + VulkanContext *vulkan_; int curWidth_; int curHeight_; -}; \ No newline at end of file + + std::thread submissionThread; + std::mutex mutex_; + std::condition_variable condVar_; + + struct SwapchainImageData { + VkImage image; + VkImageView view; + }; + std::vector framebuffers_; + std::vector swapchainImages_; + uint32_t swapchainImageCount; + uint32_t current_buffer = 0; + VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; + struct DepthBufferInfo { + VkFormat format; + VkImage image = VK_NULL_HANDLE; + VkDeviceMemory mem = VK_NULL_HANDLE; + VkImageView view = VK_NULL_HANDLE; + }; + DepthBufferInfo depth_; + // Interpreter state + VkFramebuffer curFramebuffer_; + VkRenderPass curRenderPass_; + +}; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index c174e5898e..896067b97e 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -94,7 +94,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra InitDeviceObjects(); - tessDataTransfer = new TessellationDataTransferVulkan(vulkan); + tessDataTransfer = new TessellationDataTransferVulkan(vulkan, draw); } void DrawEngineVulkan::InitDeviceObjects() { @@ -294,10 +294,11 @@ void DrawEngineVulkan::BeginFrame() { // TODO : Find a better place to do this. if (!nullTexture_) { + VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); nullTexture_ = new VulkanTexture(vulkan_); int w = 8; int h = 8; - nullTexture_->CreateDirect(w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); uint32_t bindOffset; VkBuffer bindBuf; @@ -308,8 +309,8 @@ void DrawEngineVulkan::BeginFrame() { data[y*w + x] = 0; // black } } - nullTexture_->UploadMip(0, w, h, bindBuf, bindOffset, w); - nullTexture_->EndCreate(); + nullTexture_->UploadMip(cmdInit, 0, w, h, bindBuf, bindOffset, w); + nullTexture_->EndCreate(cmdInit); } DirtyAllUBOs(); @@ -643,13 +644,15 @@ void DrawEngineVulkan::DoFlush() { // TODO: Should be enough to update this once per frame? gpuStats.numTrackedVertexArrays = (int)vai_.size(); + /* VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); if (cmd != lastCmd_) { lastPipeline_ = nullptr; lastCmd_ = cmd; // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); - } + }*/ + VkCommandBuffer cmd = VK_NULL_HANDLE; VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS); if (!rp) @@ -1087,11 +1090,12 @@ void DrawEngineVulkan::UpdateUBOs(FrameData *frame) { void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) { int rowPitch; + VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // Position if (prevSize < size) { prevSize = size; - data_tex[0]->CreateDirect(size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + data_tex[0]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } pos = (float *)data_tex[0]->Lock(0, &rowPitch); @@ -1100,7 +1104,7 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po if (prevSizeTex < size) { prevSizeTex = size; - data_tex[1]->CreateDirect(size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + data_tex[1]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } tex = (float *)data_tex[1]->Lock(0, &rowPitch); } @@ -1110,19 +1114,20 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po if (prevSizeCol < sizeColor) { prevSizeCol = sizeColor; - data_tex[2]->CreateDirect(sizeColor, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + data_tex[2]->CreateDirect(cmd, sizeColor, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } col = (float *)data_tex[2]->Lock(0, &rowPitch); } void DrawEngineVulkan::TessellationDataTransferVulkan::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) { + VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // Position - data_tex[0]->Unlock(); + data_tex[0]->Unlock(cmd); // Texcoords if (hasTexCoords) - data_tex[1]->Unlock(); + data_tex[1]->Unlock(cmd); // Color - data_tex[2]->Unlock(); + data_tex[2]->Unlock(cmd); } diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index a5387dfb9d..098a16a5f6 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -255,14 +255,15 @@ private: // Hardware tessellation class TessellationDataTransferVulkan : public TessellationDataTransfer { private: - VulkanContext *vulkan; + VulkanContext *vulkan_; + Draw::DrawContext *draw_; VulkanTexture *data_tex[3]; VkSampler sampler; public: - TessellationDataTransferVulkan(VulkanContext *vulkan) - : TessellationDataTransfer(), vulkan(vulkan), data_tex(), sampler() { + TessellationDataTransferVulkan(VulkanContext *vulkan, Draw::DrawContext *draw) + : TessellationDataTransfer(), vulkan_(vulkan), draw_(draw), data_tex(), sampler() { for (int i = 0; i < 3; i++) - data_tex[i] = new VulkanTexture(vulkan); + data_tex[i] = new VulkanTexture(vulkan_); CreateSampler(); } @@ -270,7 +271,7 @@ private: for (int i = 0; i < 3; i++) delete data_tex[i]; - vulkan->Delete().QueueDeleteSampler(sampler); + vulkan_->Delete().QueueDeleteSampler(sampler); } void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; void PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) override; @@ -301,7 +302,7 @@ private: samp.minLod = 0.0f; samp.mipLodBias = 0.0f; - VkResult res = vkCreateSampler(vulkan->GetDevice(), &samp, nullptr, &sampler); + VkResult res = vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &sampler); assert(res == VK_SUCCESS); } }; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 335edcf44e..96e08b8923 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -28,6 +28,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanMemory.h" #include "Common/Vulkan/VulkanImage.h" +#include "Common/Vulkan/VulkanRenderManager.h" #include "Common/ColorConv.h" #include "Core/Host.h" #include "Core/MemMap.h" @@ -131,7 +132,7 @@ void FramebufferManagerVulkan::InitDeviceObjects() { assert(vsBasicTex_ != VK_NULL_HANDLE); // Prime the 2D pipeline cache. - vulkan2D_.GetPipeline(pipelineCache2D_, vulkan_->GetSurfaceRenderPass(), vsBasicTex_, fsBasicTex_); + vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS), vsBasicTex_, fsBasicTex_); VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; @@ -234,13 +235,15 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor drawPixelsTex_ = nullptr; } + VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); + if (!drawPixelsTex_) { drawPixelsTex_ = new VulkanTexture(vulkan_); - drawPixelsTex_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); // Initialize backbuffer texture for DrawPixels drawPixelsTexFormat_ = srcPixelFormat; } else { - drawPixelsTex_->TransitionForUpload(); + drawPixelsTex_->TransitionForUpload(initCmd); } // TODO: We can just change the texture format and flip some bits around instead of this. @@ -297,25 +300,22 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor VkBuffer buffer; size_t offset = frameData_[curFrame_].push_->Push(data, width * height * 4, &buffer); - drawPixelsTex_->UploadMip(0, width, height, buffer, (uint32_t)offset, width); - drawPixelsTex_->EndCreate(); + drawPixelsTex_->UploadMip(initCmd, 0, width, height, buffer, (uint32_t)offset, width); + drawPixelsTex_->EndCreate(initCmd); overrideImageView_ = drawPixelsTex_->GetImageView(); } void FramebufferManagerVulkan::SetViewport2D(int x, int y, int w, int h) { - VkViewport vp; - vp.minDepth = 0.0; - vp.maxDepth = 1.0; - vp.x = (float)x; - vp.y = (float)y; - vp.width = (float)w; - vp.height = (float)h; - + Draw::Viewport vp; + vp.MinDepth = 0.0; + vp.MaxDepth = 1.0; + vp.TopLeftX = (float)x; + vp.TopLeftY = (float)y; + vp.Width = (float)w; + vp.Height = (float)h; // Since we're about to override it. - draw_->FlushState(); - VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); - vkCmdSetViewport(cmd, 0, 1, &vp); + draw_->SetViewports(1, &vp); } void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { @@ -359,18 +359,15 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa // TODO: Should probably use draw_ directly and not go low level VulkanPushBuffer *push = frameData_[curFrame_].push_; - - VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); if ((flags & DRAWTEX_KEEP_TEX) == 0) overrideImageView_ = VK_NULL_HANDLE; - vulkan2D_.BindDescriptorSet(cmd, view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, cur2DPipeline_); + VkDescriptorSet descSet = vulkan2D_.GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); VkBuffer vbuffer; VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer); - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuffer, &offset); - vkCmdDraw(cmd, 4, 1, 0, 0); + renderManager->Draw(cur2DPipeline_, vulkan2D_.GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); } void FramebufferManagerVulkan::Bind2DShader() { @@ -645,18 +642,6 @@ void FramebufferManagerVulkan::BeginFrameVulkan() { frame.push_->Reset(); frame.push_->Begin(vulkan_); - - if (!useBufferedRendering_) { - // TODO: This hackery should not be necessary. Is it? Need to check. - // We only use a single command buffer in this case. - VkCommandBuffer cmd = vulkan_->GetSurfaceCommandBuffer(); - VkRect2D scissor; - scissor.offset = { 0, 0 }; - scissor.extent = { (uint32_t)pixelWidth_, (uint32_t)pixelHeight_ }; - vkCmdSetScissor(cmd, 0, 1, &scissor); - } else { - // Each render pass will set up scissor again. - } } void FramebufferManagerVulkan::EndFrame() { diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 9cea56729b..2ae17c63b1 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -466,6 +466,7 @@ VkFormat ToVulkanFormat(ReplacedTextureFormat fmt) { void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceImages) { entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK; + VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // For the estimate, we assume cluts always point to 8888 for simplicity. cacheSizeEstimate_ += EstimateTexMemoryUsage(entry); @@ -588,7 +589,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm break; } - bool allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + bool allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); if (!allocSuccess && !lowMemoryMode_) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; @@ -607,7 +608,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm scaleFactor = 1; actualFmt = dstFmt; - allocSuccess = image->CreateDirect(w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); } if (!allocSuccess) { @@ -615,7 +616,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm entry->vkTex = nullptr; } } else { - entry->vkTex->texture_->TransitionForUpload(); + entry->vkTex->texture_->TransitionForUpload(cmdInit); } lastBoundTexture = entry->vkTex; @@ -652,7 +653,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm } else { if (fakeMipmap) { LoadTextureLevel(*entry, (uint8_t *)data, stride, level, scaleFactor, dstFmt); - entry->vkTex->texture_->UploadMip(0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); + entry->vkTex->texture_->UploadMip(cmdInit, 0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); break; } else { LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); @@ -661,7 +662,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm replacer_.NotifyTextureDecoded(replacedInfo, data, stride, i, mipWidth, mipHeight); } } - entry->vkTex->texture_->UploadMip(i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); + entry->vkTex->texture_->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); } if (maxLevel == 0) { @@ -674,7 +675,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm } } - entry->vkTex->texture_->EndCreate(); + entry->vkTex->texture_->EndCreate(cmdInit); gstate_c.SetTextureFullAlpha(entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL); gstate_c.SetTextureSimpleAlpha(entry->GetAlphaStatus() != TexCacheEntry::STATUS_ALPHA_UNKNOWN); diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index b9d8e905e9..576315ff9b 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -27,11 +27,11 @@ VulkanFBO::~VulkanFBO() { delete depthStencil_; } -void VulkanFBO::Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) { +void VulkanFBO::Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) { color_ = new VulkanTexture(vulkan); VkImageCreateFlags flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - color_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, nullptr); - depthStencil_->CreateDirect(width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, nullptr); + color_->CreateDirect(cmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, nullptr); + depthStencil_->CreateDirect(cmd, width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, nullptr); VkImageView views[2] = { color_->GetImageView(), depthStencil_->GetImageView() }; @@ -334,11 +334,6 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade } } -void Vulkan2D::BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1) { - VkDescriptorSet descSet = GetDescriptorSet(tex1, sampler1, VK_NULL_HANDLE, VK_NULL_HANDLE); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 0, nullptr); -} - VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *error) { std::vector spirv; bool success = GLSLtoSPV(stage, code, spirv, error); diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 630bebd05f..fefb35544b 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -54,7 +54,7 @@ public: // Depth-format is chosen automatically depending on hardware support. // Color format will be 32-bit RGBA. - void Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat); + void Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat); VulkanTexture *GetColor() { return color_; } VulkanTexture *GetDepthStencil() { return depthStencil_; } @@ -84,15 +84,12 @@ public: void Shutdown(); VkPipeline GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShaderModule vs, VkShaderModule fs); - + VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } void BeginFrame(); void EndFrame(); VkDescriptorSet GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2); - // Simple way - void BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1); - struct Vertex { float x, y, z; float u, v; diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index e326e11c54..ef322d90e2 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -189,7 +189,7 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions); } g_Vulkan->InitSurfaceWin32(hInst, hWnd); - if (!g_Vulkan->InitObjects(true)) { + if (!g_Vulkan->InitObjects()) { Shutdown(); return false; } @@ -219,10 +219,12 @@ void WindowsVulkanContext::SwapBuffers() { void WindowsVulkanContext::Resize() { g_Vulkan->WaitUntilQueueIdle(); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); g_Vulkan->DestroyObjects(); g_Vulkan->ReinitSurfaceWin32(); - g_Vulkan->InitObjects(true); + g_Vulkan->InitObjects(); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } void WindowsVulkanContext::SwapInterval(int interval) { diff --git a/ext/native/math/dataconv.h b/ext/native/math/dataconv.h index 2801a06a40..84e9da1a7e 100644 --- a/ext/native/math/dataconv.h +++ b/ext/native/math/dataconv.h @@ -105,6 +105,17 @@ inline void CopyFloat3(float dest[3], const float src[3]) { dest[2] = src[2]; } +inline void CopyFloat4(float dest[3], const float src[3]) { +#ifdef _M_SSE + _mm_storeu_ps(dest, _mm_loadu_ps(src)); +#else + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; +#endif +} + inline void CopyFloat1To4(float dest[4], const float src) { #ifdef _M_SSE _mm_storeu_ps(dest, _mm_set_ss(src)); diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index dcbe746160..5e18ad9927 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -321,9 +321,10 @@ enum class NativeObject { BACKBUFFER_COLOR_TEX, BACKBUFFER_DEPTH_TEX, FEATURE_LEVEL, + BACKBUFFER_RENDERPASS, COMPATIBLE_RENDERPASS, CURRENT_RENDERPASS, - RENDERPASS_COMMANDBUFFER, + INIT_COMMANDBUFFER, BOUND_TEXTURE_IMAGEVIEW, RENDER_MANAGER, }; @@ -678,7 +679,7 @@ public: } virtual std::string GetInfoString(InfoField info) const = 0; - virtual uintptr_t GetNativeObject(NativeObject obj) const = 0; + virtual uintptr_t GetNativeObject(NativeObject obj) = 0; virtual void HandleEvent(Event ev, int width, int height, void *param1 = nullptr, void *param2 = nullptr) = 0; diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 554bdca80b..e7ca762369 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -116,7 +116,7 @@ public: } } - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { case NativeObject::DEVICE: return (uintptr_t)device_; diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index ad40c89a44..118bd48fab 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -529,7 +529,7 @@ public: void DrawUP(const void *vdata, int vertexCount) override; void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal); - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { case NativeObject::CONTEXT: return (uintptr_t)d3d_; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 65dcdd4a33..1334a26e2c 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -560,7 +560,7 @@ public: } } - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { return 0; } diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 6620921b43..2d409843cd 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -319,9 +319,9 @@ struct DescriptorSetKey { class VKTexture : public Texture { public: - VKTexture(VulkanContext *vulkan, const TextureDesc &desc) + VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, const TextureDesc &desc) : vulkan_(vulkan), format_(desc.format), mipLevels_(desc.mipLevels) { - Create(desc); + Create(cmd, desc); } ~VKTexture() { @@ -331,9 +331,9 @@ public: VkImageView GetImageView() { return vkTex_->GetImageView(); } private: - void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); + void SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); - bool Create(const TextureDesc &desc); + bool Create(VkCommandBuffer cmd, const TextureDesc &desc); void Destroy() { if (vkTex_) { @@ -351,15 +351,6 @@ private: DataFormat format_; }; -// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine -// to let them have individual non-pooled allocations. Until it's not fine. We'll see. -struct VKImage { - VkImage image; - VkImageView view; - VkDeviceMemory memory; - VkImageLayout layout; -}; - class VKContext : public DrawContext { public: VKContext(VulkanContext *vulkan); @@ -434,7 +425,6 @@ public: void EndFrame() override; void FlushState() override { - ApplyDynamicState(); } void WaitRenderCompletion(Framebuffer *fbo) override; @@ -460,18 +450,20 @@ public: std::vector GetFeatureList() const override; std::vector GetExtensionList() const override; - uintptr_t GetNativeObject(NativeObject obj) const override { + uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { case NativeObject::COMPATIBLE_RENDERPASS: // Return a representative renderpass. - if (curRenderPass_ == vulkan_->GetSurfaceRenderPass()) + if (curRenderPass_ == renderManager_.GetSurfaceRenderPass()) return (uintptr_t)curRenderPass_; else - return (uintptr_t)renderPasses_[0]; + return (uintptr_t)renderManager_.GetRenderPass(0); + case NativeObject::BACKBUFFER_RENDERPASS: + return (uintptr_t)renderManager_.GetSurfaceRenderPass(); case NativeObject::CURRENT_RENDERPASS: return (uintptr_t)curRenderPass_; - case NativeObject::RENDERPASS_COMMANDBUFFER: - return (uintptr_t)cmd_; + case NativeObject::INIT_COMMANDBUFFER: + return (uintptr_t)renderManager_.GetInitCmd(); case NativeObject::BOUND_TEXTURE_IMAGEVIEW: return (uintptr_t)boundImageView_[0]; @@ -485,15 +477,6 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override; private: - void ApplyDynamicState(); - void DirtyDynamicState(); - - void EndCurrentRenderpass(); - VkCommandBuffer AllocCmdBuf(); - - static void SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - static void SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - VulkanContext *vulkan_ = nullptr; VulkanRenderManager renderManager_; @@ -508,23 +491,10 @@ private: VkPipelineLayout pipelineLayout_; VkPipelineCache pipelineCache_; - inline int RPIndex(RPAction color, RPAction depth) { - return (int)depth * 3 + (int)color; - } - - // Renderpasses, all combination of preserving or clearing or dont-care-ing fb contents. - VkRenderPass renderPasses_[9]; - VkDevice device_; VkQueue queue_; int queueFamilyIndex_; - // State to apply at the next draw call if viewportDirty or scissorDirty are true. - bool viewportDirty_; - VkViewport viewport_{}; - bool scissorDirty_; - VkRect2D scissor_; - int curWidth_ = -1; int curHeight_ = -1; @@ -538,11 +508,6 @@ private: struct FrameData { VulkanPushBuffer *pushBuffer; - VkCommandPool cmdPool_; - VkCommandBuffer cmdBufs[MAX_FRAME_COMMAND_BUFFERS]; - int startCmdBufs_; - int numCmdBufs; - // Per-frame descriptor set cache. As it's per frame and reset every frame, we don't need to // worry about invalidating descriptors pointing to deleted textures. // However! ARM is not a fan of doing it this way. @@ -559,7 +524,6 @@ private: VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; VkRenderPass curRenderPass_ = VK_NULL_HANDLE; - VkCommandBuffer cmd_ = VK_NULL_HANDLE; }; static int GetBpp(VkFormat format) { @@ -683,7 +647,7 @@ enum class TextureState { PENDING_DESTRUCTION, }; -bool VKTexture::Create(const TextureDesc &desc) { +bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc) { // Zero-sized textures not allowed. if (desc.width * desc.height * desc.depth == 0) return false; @@ -695,14 +659,14 @@ bool VKTexture::Create(const TextureDesc &desc) { vkTex_ = new VulkanTexture(vulkan_); if (desc.initData.size()) { for (int i = 0; i < (int)desc.initData.size(); i++) { - this->SetImageData(0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]); + this->SetImageData(cmd, 0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]); } } return true; } VKContext::VKContext(VulkanContext *vulkan) - : viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan), frameNum_(0), caps_{}, renderManager_(vulkan) { + : vulkan_(vulkan), frameNum_(0), caps_{}, renderManager_(vulkan) { caps_.anisoSupported = vulkan->GetFeaturesAvailable().samplerAnisotropy != 0; caps_.geometryShaderSupported = vulkan->GetFeaturesAvailable().geometryShader != 0; caps_.tesselationShaderSupported = vulkan->GetFeaturesAvailable().tessellationShader != 0; @@ -715,16 +679,6 @@ VKContext::VKContext(VulkanContext *vulkan) queue_ = vulkan->GetGraphicsQueue(); queueFamilyIndex_ = vulkan->GetGraphicsQueueFamilyIndex(); - scissor_.offset.x = 0; - scissor_.offset.y = 0; - scissor_.extent.width = pixel_xres; - scissor_.extent.height = pixel_yres; - viewport_.x = 0; - viewport_.y = 0; - viewport_.width = pixel_xres; - viewport_.height = pixel_yres; - viewport_.minDepth = 0.0f; - viewport_.maxDepth = 0.0f; memset(boundTextures_, 0, sizeof(boundTextures_)); VkDescriptorPoolSize dpTypes[2]; @@ -744,10 +698,6 @@ VKContext::VKContext(VulkanContext *vulkan) p.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex(); for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { - VkResult res = vkCreateDescriptorPool(device_, &dp, nullptr, &frame_[i].descriptorPool); - assert(VK_SUCCESS == res); - res = vkCreateCommandPool(device_, &p, nullptr, &frame_[i].cmdPool_); - assert(VK_SUCCESS == res); frame_[i].pushBuffer = new VulkanPushBuffer(vulkan_, 1024 * 1024); } @@ -780,125 +730,25 @@ VKContext::VKContext(VulkanContext *vulkan) assert(VK_SUCCESS == res); pipelineCache_ = vulkan_->CreatePipelineCache(); - - // Create a bunch of render pass objects, for normal rendering with a depth buffer, - // with clearing, without clearing, and dont-care for both depth/stencil and color, so 3*3=9 combos. - VkAttachmentDescription attachments[2] = {}; - attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].flags = 0; - - attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].flags = 0; - - VkAttachmentReference color_reference = {}; - color_reference.attachment = 0; - color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depth_reference = {}; - depth_reference.attachment = 1; - depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.flags = 0; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = NULL; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_reference; - subpass.pResolveAttachments = NULL; - subpass.pDepthStencilAttachment = &depth_reference; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = NULL; - - VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rp.attachmentCount = 2; - rp.pAttachments = attachments; - rp.subpassCount = 1; - rp.pSubpasses = &subpass; - rp.dependencyCount = 0; - rp.pDependencies = NULL; - - for (int depth = 0; depth < 3; depth++) { - switch ((RPAction)depth) { - case RPAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RPAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RPAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; - } - for (int color = 0; color < 3; color++) { - switch ((RPAction)color) { - case RPAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RPAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RPAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; - } - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((RPAction)color, (RPAction)depth)]); - } - } } VKContext::~VKContext() { - for (int i = 0; i < 9; i++) { - vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); - } // This also destroys all descriptor sets. for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].descSets_.clear(); vulkan_->Delete().QueueDeleteDescriptorPool(frame_[i].descriptorPool); frame_[i].pushBuffer->Destroy(vulkan_); delete frame_[i].pushBuffer; - vulkan_->Delete().QueueDeleteCommandPool(frame_[i].cmdPool_); } vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_); vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_); vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_); } -// Effectively wiped every frame, just allocate new ones! -VkCommandBuffer VKContext::AllocCmdBuf() { - FrameData *frame = &frame_[frameNum_]; - - if (frame->numCmdBufs >= MAX_FRAME_COMMAND_BUFFERS) - Crash(); - - if (frame->cmdBufs[frame->numCmdBufs]) { - VkCommandBuffer cmdBuf = frame->cmdBufs[frame->numCmdBufs++]; - if (!cmdBuf) - Crash(); - return cmdBuf; - } - - VkCommandBufferAllocateInfo alloc{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - alloc.commandBufferCount = 1; - alloc.commandPool = frame->cmdPool_; - alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - VkResult result = vkAllocateCommandBuffers(vulkan_->GetDevice(), &alloc, &frame->cmdBufs[frame->numCmdBufs]); - assert(result == VK_SUCCESS); - VkCommandBuffer cmdBuf = frame->cmdBufs[frame->numCmdBufs++]; - if (!cmdBuf) - Crash(); - return cmdBuf; -} - void VKContext::BeginFrame() { renderManager_.BeginFrameWrites(); FrameData &frame = frame_[frameNum_]; - frame.startCmdBufs_ = 0; - frame.numCmdBufs = 0; - vkResetCommandPool(vulkan_->GetDevice(), frame.cmdPool_, 0); push_ = frame.pushBuffer; // OK, we now know that nothing is reading from this frame's data pushbuffer, @@ -909,10 +759,7 @@ void VKContext::BeginFrame() { VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0); assert(result == VK_SUCCESS); - scissor_.extent.width = pixel_xres; - scissor_.extent.height = pixel_yres; - scissorDirty_ = true; - viewportDirty_ = true; + SetScissorRect(0, 0, pixel_xres, pixel_yres); } void VKContext::WaitRenderCompletion(Framebuffer *fbo) { @@ -920,31 +767,16 @@ void VKContext::WaitRenderCompletion(Framebuffer *fbo) { } void VKContext::EndFrame() { - EndCurrentRenderpass(); - renderManager_.Flush(cmd_); - - if (cmd_) - Crash(); - - // Cap off and submit all the command buffers we recorded during the frame. - FrameData &frame = frame_[frameNum_]; - for (int i = frame.startCmdBufs_; i < frame.numCmdBufs; i++) { - vkEndCommandBuffer(frame.cmdBufs[i]); - vulkan_->QueueBeforeSurfaceRender(frame.cmdBufs[i]); - } - frame.startCmdBufs_ = frame.numCmdBufs; - // Stop collecting data in the frame's data pushbuffer. push_->End(); + renderManager_.Flush(); renderManager_.EndFrame(); frameNum_++; if (frameNum_ >= vulkan_->GetInflightFrames()) frameNum_ = 0; push_ = nullptr; - - DirtyDynamicState(); } VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { @@ -1075,7 +907,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically. info.layout = pipelineLayout_; info.subpass = 0; - info.renderPass = vulkan_->GetSurfaceRenderPass(); + info.renderPass = renderManager_.GetSurfaceRenderPass(); // OK, need to create a new pipeline. VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 1, &info, nullptr, &pipeline->vkpipeline); @@ -1092,22 +924,20 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { } void VKContext::SetScissorRect(int left, int top, int width, int height) { - scissor_.offset.x = left; - scissor_.offset.y = top; - scissor_.extent.width = width; - scissor_.extent.height = height; - scissorDirty_ = true; + VkRect2D scissor{ {left, top}, {(uint32_t)width, (uint32_t)height} }; + renderManager_.SetScissor(scissor); } void VKContext::SetViewports(int count, Viewport *viewports) { if (count > 0) { - viewport_.x = viewports[0].TopLeftX; - viewport_.y = viewports[0].TopLeftY; - viewport_.width = viewports[0].Width; - viewport_.height = viewports[0].Height; - viewport_.minDepth = viewports[0].MinDepth; - viewport_.maxDepth = viewports[0].MaxDepth; - viewportDirty_ = true; + VkViewport viewport; + viewport.x = viewports[0].TopLeftX; + viewport.y = viewports[0].TopLeftY; + viewport.width = viewports[0].Width; + viewport.height = viewports[0].Height; + viewport.minDepth = viewports[0].MinDepth; + viewport.maxDepth = viewports[0].MaxDepth; + renderManager_.SetViewport(viewport); } } @@ -1115,22 +945,6 @@ void VKContext::SetBlendFactor(float color[4]) { renderManager_.SetBlendFactor(color); } -void VKContext::ApplyDynamicState() { - if (scissorDirty_) { - renderManager_.SetScissor(scissor_); - scissorDirty_ = false; - } - if (viewportDirty_) { - renderManager_.SetViewport(viewport_); - viewportDirty_ = false; - } -} - -void VKContext::DirtyDynamicState() { - scissorDirty_ = true; - viewportDirty_ = true; -} - InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) { VKInputLayout *vl = new VKInputLayout(); vl->visc = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; @@ -1156,10 +970,10 @@ InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) { } Texture *VKContext::CreateTexture(const TextureDesc &desc) { - return new VKTexture(vulkan_, desc); + return new VKTexture(vulkan_, renderManager_.GetInitCmd(), desc); } -void VKTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { +void VKTexture::SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { VkFormat vulkanFormat = DataFormatToVulkan(format_); if (stride == 0) { stride = width * (int)DataFormatSizeInBytes(format_); @@ -1172,7 +986,7 @@ void VKTexture::SetImageData(int x, int y, int z, int width, int height, int dep for (int y = 0; y < height; y++) { memcpy(dstData + rowPitch * y, data + stride * y, width * bytesPerPixel); } - vkTex_->Unlock(); + vkTex_->Unlock(cmd); } inline void CopySide(VkStencilOpState &dest, const StencilSide &src) { @@ -1297,8 +1111,6 @@ void VKContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { } void VKContext::Draw(int vertexCount, int offset) { - ApplyDynamicState(); - VKBuffer *vbuf = curVBuffers_[0]; VkBuffer vulkanVbuf; @@ -1312,8 +1124,6 @@ void VKContext::Draw(int vertexCount, int offset) { } void VKContext::DrawIndexed(int vertexCount, int offset) { - ApplyDynamicState(); - VKBuffer *ibuf = static_cast(curIBuffer_); VKBuffer *vbuf = static_cast(curVBuffers_[0]); @@ -1328,13 +1138,10 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { } void VKContext::DrawUP(const void *vdata, int vertexCount) { - ApplyDynamicState(); - VkBuffer vulkanVbuf, vulkanUBObuf; size_t vbBindOffset = push_->Push(vdata, vertexCount * curPipeline_->stride[0], &vulkanVbuf); uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); - VkBuffer buffers[1] = { vulkanVbuf }; VkDeviceSize offsets[1] = { vbBindOffset }; VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); @@ -1445,585 +1252,82 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const { } } -void CreateImage(VulkanContext *vulkan, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { - VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - ici.arrayLayers = 1; - ici.mipLevels = 1; - ici.extent.width = width; - ici.extent.height = height; - ici.extent.depth = 1; - ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - ici.imageType = VK_IMAGE_TYPE_2D; - ici.samples = VK_SAMPLE_COUNT_1_BIT; - ici.tiling = VK_IMAGE_TILING_OPTIMAL; - ici.format = format; - ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - if (color) { - ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - } else { - ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - } - vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image); - - // TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers - - VkMemoryRequirements memreq; - vkGetImageMemoryRequirements(vulkan->GetDevice(), img.image, &memreq); - - VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - alloc.allocationSize = memreq.size; - vulkan->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex); - VkResult res = vkAllocateMemory(vulkan->GetDevice(), &alloc, nullptr, &img.memory); - assert(res == VK_SUCCESS); - res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0); - assert(res == VK_SUCCESS); - - VkImageAspectFlags aspects = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - - VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; - ivci.format = ici.format; - ivci.image = img.image; - ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; - ivci.subresourceRange.aspectMask = aspects; - ivci.subresourceRange.layerCount = 1; - ivci.subresourceRange.levelCount = 1; - res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.view); - assert(res == VK_SUCCESS); - - VkPipelineStageFlagBits dstStage; - VkAccessFlagBits dstAccessMask; - switch (initialLayout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; - break; - } - - TransitionImageLayout2(vulkan->GetInitCommandBuffer(), img.image, aspects, - VK_IMAGE_LAYOUT_UNDEFINED, initialLayout, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, dstStage, - 0, dstAccessMask); -} - // A VKFramebuffer is a VkFramebuffer (note caps difference) plus all the textures it owns. // It also has a reference to the command buffer that it was last rendered to with. // If it needs to be transitioned, and the frame number matches, use it, otherwise // use this frame's init command buffer. class VKFramebuffer : public Framebuffer { public: - VKFramebuffer(VulkanContext *vk) : vulkan_(vk) {} + VKFramebuffer(VulkanContext *vk, VKRFramebuffer *fb) : vulkan_(vk), buf_(fb) {} ~VKFramebuffer() { - vulkan_->Delete().QueueDeleteImage(color.image); - vulkan_->Delete().QueueDeleteImage(depth.image); - vulkan_->Delete().QueueDeleteImageView(color.view); - vulkan_->Delete().QueueDeleteImageView(depth.view); - vulkan_->Delete().QueueDeleteDeviceMemory(color.memory); - vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory); - vulkan_->Delete().QueueDeleteFramebuffer(framebuf); + delete buf_; } - VkFramebuffer framebuf = VK_NULL_HANDLE; - VKImage color{}; - VKImage depth{}; - int width = 0; - int height = 0; - - // These belong together, see above. - VkCommandBuffer cmdBuf = VK_NULL_HANDLE; - int frameCount = 0; - + VKRFramebuffer *GetFB() const { return buf_; } private: + VKRFramebuffer *buf_; VulkanContext *vulkan_; }; Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { - VKFramebuffer *fb = new VKFramebuffer(vulkan_); - fb->width = desc.width; - fb->height = desc.height; - - CreateImage(vulkan_, fb->color, fb->width, fb->height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true); - CreateImage(vulkan_, fb->depth, fb->width, fb->height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false); - - VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - VkImageView views[2]{}; - - fbci.renderPass = renderPasses_[0]; - fbci.attachmentCount = 2; - fbci.pAttachments = views; - views[0] = fb->color.view; - views[1] = fb->depth.view; - fbci.width = fb->width; - fbci.height = fb->height; - fbci.layers = 1; - - vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &fb->framebuf); + VkCommandBuffer cmd = renderManager_.GetInitCmd(); + VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetRenderPass(0), desc.width, desc.height); + VKFramebuffer *fb = new VKFramebuffer(vulkan_, vkrfb); return fb; } void VKContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y, int z, Framebuffer *dstfb, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { - // Can't copy during render passes. - EndCurrentRenderpass(); - VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - VkImageCopy copy{}; - copy.srcOffset.x = x; - copy.srcOffset.y = y; - copy.srcOffset.z = z; - copy.srcSubresource.mipLevel = level; - copy.srcSubresource.layerCount = 1; - copy.dstOffset.x = dstX; - copy.dstOffset.y = dstY; - copy.dstOffset.z = dstZ; - copy.dstSubresource.mipLevel = dstLevel; - copy.dstSubresource.layerCount = 1; - copy.extent.width = width; - copy.extent.height = height; - copy.extent.depth = depth; - - // We're gonna tack copies onto the src's command buffer, if it's from this frame. - // If from a previous frame, just do it in frame init. - VkCommandBuffer cmd = src->cmdBuf; - if (src->frameCount != frameNum_) { - // TODO: What about the case where dst->frameCount == frameNum_ here? - // That will cause bad ordering. We'll have to allocate a new command buffer and assign it to dest. - cmd = vulkan_->GetInitCommandBuffer(); - } - - VkImageMemoryBarrier srcBarriers[2]{}; - VkImageMemoryBarrier dstBarriers[2]{}; - int srcCount = 0; - int dstCount = 0; - - // First source barriers. - if (channelBits & FB_COLOR_BIT) { - if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - } - - // We can't copy only depth or only stencil unfortunately. - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - } - - // TODO: Fix the pipe bits to be bit less conservative. - if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); - } - if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); - } - - if (channelBits & FB_COLOR_BIT) { - copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©); - } - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - copy.srcSubresource.aspectMask = 0; - copy.dstSubresource.aspectMask = 0; - if (channelBits & FB_DEPTH_BIT) { - copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (channelBits & FB_STENCIL_BIT) { - copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©); - } + renderManager_.CopyFramebuffer(src->GetFB(), VkRect2D{ {x, y}, {(uint32_t)width, (uint32_t)height } }, dst->GetFB(), VkOffset2D{ dstX, dstY }); } bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - // We're gonna tack blits onto the src's command buffer, if it's from this frame. - // If from a previous frame, just do it in frame init. - VkCommandBuffer cmd = src->cmdBuf; - if (src->frameCount != frameNum_) { - // TODO: What about the case where dst->frameCount == frameNum_ here? - // That will cause bad ordering. We'll have to allocate a new command buffer and assign it to dest. - cmd = vulkan_->GetInitCommandBuffer(); - } - VkImageMemoryBarrier srcBarriers[2]{}; - VkImageMemoryBarrier dstBarriers[2]{}; - int srcCount = 0; - int dstCount = 0; - - VkImageBlit blit{}; - blit.srcOffsets[0].x = srcX1; - blit.srcOffsets[0].y = srcY1; - blit.srcOffsets[0].z = 0; - blit.srcOffsets[1].x = srcX2; - blit.srcOffsets[1].y = srcY2; - blit.srcOffsets[1].z = 1; - blit.srcSubresource.mipLevel = 0; - blit.srcSubresource.layerCount = 1; - blit.dstOffsets[0].x = dstX1; - blit.dstOffsets[0].y = dstY1; - blit.dstOffsets[0].z = 0; - blit.dstOffsets[1].x = dstX2; - blit.dstOffsets[1].y = dstY2; - blit.dstOffsets[1].z = 1; - blit.dstSubresource.mipLevel = 0; - blit.dstSubresource.layerCount = 1; - - // First source barriers. - if (channelBits & FB_COLOR_BIT) { - if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); - } - } - - // We can't copy only depth or only stencil unfortunately. - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - } - - // TODO: Fix the pipe bits to be bit less conservative. - if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); - } - if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); - } - - if (channelBits & FB_COLOR_BIT) { - blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); - } - if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) { - blit.srcSubresource.aspectMask = 0; - blit.dstSubresource.aspectMask = 0; - if (channelBits & FB_DEPTH_BIT) { - blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (channelBits & FB_STENCIL_BIT) { - blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - vkCmdBlitImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &blit, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); - } + renderManager_.BlitFramebuffer(src->GetFB(), VkRect2D{ {srcX1, srcY1}, {(uint32_t)(srcX2 - srcX1), (uint32_t)(srcY2 - srcY1) } }, dst->GetFB(), VkRect2D{ {dstX1, dstY1}, {(uint32_t)(dstX2 - dstX1), (uint32_t)(dstY2 - dstY1) } }, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); return true; } -void VKContext::SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = img.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; - switch (img.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - break; - default: - Crash(); - } - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - img.layout = barrier.newLayout; -} - -void VKContext::SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = img.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; - switch (img.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - break; - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - break; - default: - Crash(); - } - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - img.layout = barrier.newLayout; -} - -void VKContext::EndCurrentRenderpass() { - if (curRenderPass_ != VK_NULL_HANDLE) { - vkCmdEndRenderPass(cmd_); - curRenderPass_ = VK_NULL_HANDLE; - curFramebuffer_ = VK_NULL_HANDLE; - cmd_ = VK_NULL_HANDLE; - } -} - void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { - VkFramebuffer framebuf; - VkCommandBuffer cmdBuf; - int w; - int h; - VkImageLayout prevLayout; - if (fbo) { - VKFramebuffer *fb = (VKFramebuffer *)fbo; - framebuf = fb->framebuf; - w = fb->width; - h = fb->height; - prevLayout = fb->color.layout; - cmdBuf = fb->cmdBuf; - } else { - framebuf = vulkan_->GetSurfaceFramebuffer(); - w = vulkan_->GetWidth(); - h = vulkan_->GetHeight(); - cmdBuf = vulkan_->GetSurfaceCommandBuffer(); - } - - if (framebuf == curFramebuffer_) { - if (framebuf == 0) - Crash(); - if (!curRenderPass_) - Crash(); - // If we're asking to clear, but already bound, we'll just keep it bound but send a clear command. - // We will try to avoid this as much as possible. - VkClearAttachment clear[2]{}; - int count = 0; - if (rp.color == RPAction::CLEAR) { - clear[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - Uint8x4ToFloat4(rp.clearColor, clear[count].clearValue.color.float32); - clear[count].colorAttachment = 0; - count++; - } - if (rp.depth == RPAction::CLEAR) { - clear[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - clear[count].clearValue.depthStencil.depth = rp.clearDepth; - clear[count].clearValue.depthStencil.stencil = rp.clearStencil; - clear[count].colorAttachment = 0; - count++; - } - if (count > 0) { - VkClearRect rc{ { 0,0,(uint32_t)w,(uint32_t)h }, 0, 1 }; - vkCmdClearAttachments(cmdBuf, count, clear, 1, &rc); - } - // We're done. - return; - } - - // OK, we're switching framebuffers. - EndCurrentRenderpass(); - VkRenderPass renderPass; - int numClearVals = 0; - VkClearValue clearVal[2]; - memset(clearVal, 0, sizeof(clearVal)); - if (fbo) { - VKFramebuffer *fb = (VKFramebuffer *)fbo; - fb->cmdBuf = AllocCmdBuf(); - if (!fb->cmdBuf) - Crash(); - fb->frameCount = frameNum_; - cmd_ = fb->cmdBuf; - VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - VkResult res = vkBeginCommandBuffer(cmd_, &begin); - assert(res == VK_SUCCESS); - // Now, if the image needs transitioning, let's transition. - // The backbuffer does not, that's handled by VulkanContext. - if (fb->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - VkAccessFlagBits srcAccessMask; - VkPipelineStageFlagBits srcStage; - switch (fb->color.layout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_UNDEFINED: - srcAccessMask = (VkAccessFlagBits)0; - srcStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - break; - default: - assert(0); - break; - } - TransitionImageLayout2(cmd_, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, - fb->color.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - fb->color.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } - if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - VkAccessFlagBits srcAccessMask; - VkPipelineStageFlagBits srcStage; - switch (fb->depth.layout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_UNDEFINED: - srcAccessMask = (VkAccessFlagBits)0; - srcStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - break; - default: - assert(0); - break; - } - TransitionImageLayout2(cmd_, fb->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, - fb->depth.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); - fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } - - renderPass = renderPasses_[RPIndex(rp.color, rp.depth)]; - // ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); - if (rp.color == RPAction::CLEAR) { - Uint8x4ToFloat4(rp.clearColor, clearVal[0].color.float32); - numClearVals = 1; - } - if (rp.depth == RPAction::CLEAR) { - clearVal[1].depthStencil.depth = rp.clearDepth; - clearVal[1].depthStencil.stencil = rp.clearStencil; - numClearVals = 2; - } - } else { - cmd_ = vulkan_->GetSurfaceCommandBuffer(); - renderPass = vulkan_->GetSurfaceRenderPass(); - numClearVals = 2; - } - - VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - rp_begin.renderPass = renderPass; - rp_begin.framebuffer = framebuf; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = w; - rp_begin.renderArea.extent.height = h; - rp_begin.clearValueCount = numClearVals; - rp_begin.pClearValues = numClearVals ? clearVal : nullptr; - vkCmdBeginRenderPass(cmd_, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); - curFramebuffer_ = framebuf; - curRenderPass_ = renderPass; - curWidth_ = w; - curHeight_ = h; + VKFramebuffer *fb = (VKFramebuffer *)fbo; + renderManager_.BindFramebufferAsRenderTarget(fb->GetFB()); } // color must be 0, for now. void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) { VKFramebuffer *fb = (VKFramebuffer *)fbo; - boundImageView_[0] = fb->color.view; - // If we already have the right layout, nothing else to do. - if (fb->color.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - return; - VkCommandBuffer transitionCmdBuf; - if (fb->cmdBuf && fb->frameCount == frameNum_) { - // If the framebuffer has a "live" command buffer, we can directly use it to transition it for sampling. - transitionCmdBuf = fb->cmdBuf; - } else { - // If not, we can just do it at the "start" of the frame. - transitionCmdBuf = vulkan_->GetInitCommandBuffer(); - } - - VkAccessFlags srcAccessMask; - VkPipelineStageFlags srcStage; - switch (fb->color.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - } - TransitionImageLayout2(transitionCmdBuf, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, - fb->color.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - srcAccessMask, VK_ACCESS_SHADER_READ_BIT); - fb->color.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + int aspect = 0; + if (channelBit & FBChannel::FB_COLOR_BIT) aspect |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBit & FBChannel::FB_DEPTH_BIT) aspect |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBit & FBChannel::FB_STENCIL_BIT) aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; + renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); } uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { - // TODO: Insert transition at the end of the previous command buffer, or the one that rendered to it last. - VKFramebuffer *fb = (VKFramebuffer *)fbo; - return (uintptr_t)fb->color.image; + return 0; } void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { VKFramebuffer *fb = (VKFramebuffer *)fbo; if (fb) { - *w = fb->width; - *h = fb->height; + *w = fb->GetFB()->width; + *h = fb->GetFB()->height; } else { - *w = vulkan_->GetWidth(); - *h = vulkan_->GetHeight(); + *w = vulkan_->GetBackbufferWidth(); + *h = vulkan_->GetBackbufferHeight(); } } void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void *param2) { + switch (ev) { + case Event::LOST_BACKBUFFER: + break; + case Event::GOT_BACKBUFFER: + break; + } // Noop } From 833916a9069804607446a6523a983bdbf54607d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 12:55:30 +0200 Subject: [PATCH 053/111] A multitude of fixes. The UI now works correctly, though of course ingame still broken. --- Common/Vulkan/VulkanMemory.cpp | 2 +- Common/Vulkan/VulkanRenderManager.cpp | 325 +++++++++++++------------- Common/Vulkan/VulkanRenderManager.h | 112 ++++----- Windows/GPU/WindowsVulkanContext.cpp | 6 +- ext/native/thin3d/thin3d_vulkan.cpp | 28 ++- 5 files changed, 241 insertions(+), 232 deletions(-) diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp index 03983c62a6..260be7d9d6 100644 --- a/Common/Vulkan/VulkanMemory.cpp +++ b/Common/Vulkan/VulkanMemory.cpp @@ -47,9 +47,9 @@ bool VulkanPushBuffer::AddBuffer() { return false; } + // Make validation happy. VkMemoryRequirements reqs; vkGetBufferMemoryRequirements(device_, info.buffer, &reqs); - // TODO: We really should use memoryTypeIndex here.. // Okay, that's the buffer. Now let's allocate some memory for it. diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp index 734ae34ac8..eab92174aa 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -3,7 +3,7 @@ #include "VulkanRenderManager.h" #include "VulkanContext.h" -void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; ici.arrayLayers = 1; ici.mipLevels = 1; @@ -74,10 +74,17 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int w } VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) { + VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + semaphoreCreateInfo.flags = 0; + VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); + assert(res == VK_SUCCESS); + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore); + assert(res == VK_SUCCESS); + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; cmd_pool_info.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex(); - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPool); assert(res == VK_SUCCESS); @@ -93,23 +100,23 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan frameData_[i].initCmd = cmdBuf[1]; frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on } +} - VkSwapchainKHR swapChain = vulkan_->GetSwapchain(); - VkResult res = vkGetSwapchainImagesKHR(vulkan->GetDevice(), swapChain, &swapchainImageCount, nullptr); +void VulkanRenderManager::CreateBackbuffers() { + VkResult res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, nullptr); assert(res == VK_SUCCESS); - ILOG("Vulkan swapchain image count: %d", swapchainImageCount); + ILOG("Vulkan swapchain image count: %d", swapchainImageCount_); - VkImage* swapchainImages = new VkImage[swapchainImageCount]; - assert(swapchainImages); - res = vkGetSwapchainImagesKHR(vulkan->GetDevice(), swapChain, &swapchainImageCount, swapchainImages); + VkImage* swapchainImages = new VkImage[swapchainImageCount_]; + res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, swapchainImages); assert(res == VK_SUCCESS); - VkCommandBuffer cmdInit = frameData_[0].initCmd; - frameData_[0].hasInitCommands = true; + VkCommandBuffer cmdInit = GetInitCmd(); - for (uint32_t i = 0; i < swapchainImageCount; i++) { + for (uint32_t i = 0; i < swapchainImageCount_; i++) { SwapchainImageData sc_buffer; + sc_buffer.image = swapchainImages[i]; VkImageViewCreateInfo color_image_view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; color_image_view.format = vulkan_->GetSwapchainFormat(); @@ -124,8 +131,7 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan color_image_view.subresourceRange.layerCount = 1; color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; color_image_view.flags = 0; - - sc_buffer.image = swapchainImages[i]; + color_image_view.image = sc_buffer.image; // Pre-set them to PRESENT_SRC_KHR, as the first thing we do after acquiring // in image to render to will be to transition them away from that. @@ -135,33 +141,36 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - color_image_view.image = sc_buffer.image; - res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, NULL, &sc_buffer.view); swapchainImages_.push_back(sc_buffer); assert(res == VK_SUCCESS); } delete[] swapchainImages; - current_buffer = 0; + current_buffer = -1; - VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - semaphoreCreateInfo.flags = 0; - res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); - assert(res == VK_SUCCESS); - res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore); - assert(res == VK_SUCCESS); + InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. + InitBackbufferRenderPass(); // Must be before InitFramebuffers. + InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + InitRenderpasses(); + curWidth_ = -1; + curHeight_ = -1; +} - InitDepthStencilBuffer(cmdInit); - InitSurfaceRenderPass(); +void VulkanRenderManager::DestroyBackbuffers() { + VkDevice device = vulkan_->GetDevice(); + for (uint32_t i = 0; i < swapchainImageCount_; i++) { + vulkan_->Delete().QueueDeleteImageView(swapchainImages_[i].view); + } + vulkan_->Delete().QueueDeleteImageView(depth_.view); + vulkan_->Delete().QueueDeleteImage(depth_.image); + vulkan_->Delete().QueueDeleteDeviceMemory(depth_.mem); + swapchainImages_.clear(); } VulkanRenderManager::~VulkanRenderManager() { VkDevice device = vulkan_->GetDevice(); - for (uint32_t i = 0; i < swapchainImageCount; i++) { - vkDestroyImageView(device, swapchainImages_[i].view, nullptr); - } - swapchainImages_.clear(); + vulkan_->WaitUntilQueueIdle(); vkDestroySemaphore(device, acquireSemaphore_, nullptr); vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { @@ -176,58 +185,64 @@ VulkanRenderManager::~VulkanRenderManager() { vkDestroyFramebuffer(device, framebuffers_[i], nullptr); } framebuffers_.clear(); - for (int i = 0; i < 9; i++) { - vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); + for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { + // vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); + vkDestroyRenderPass(device, renderPasses_[i], nullptr); } } +// TODO: Activate this code. void VulkanRenderManager::ThreadFunc() { while (true) { std::unique_lock lock(mutex_); condVar_.wait(lock); - if (steps_.size()) { - stepsOnThread_ = std::move(steps_); - } - // ... + Flush(); } } -void VulkanRenderManager::BeginFrameWrites() { - vulkan_->BeginFrame(); +void VulkanRenderManager::BeginFrame() { VkDevice device = vulkan_->GetDevice(); FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. + // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, ¤t_buffer); + assert(res == VK_SUCCESS); + // Make sure the very last command buffer from the frame before the previous has been fully executed. vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); - // Reset both command buffers in one fell swoop. - // Note that on the first frame, there might already be commands so don't reset in that case. - if (!frameData.hasInitCommands) { - vkResetCommandPool(vulkan_->GetDevice(), frameData.cmdPool, 0); - } + // Must be after the fence - this performs deletes. + vulkan_->BeginFrame(); - VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin.pInheritanceInfo = nullptr; - VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + insideFrame_ = true; } VkCommandBuffer VulkanRenderManager::GetInitCmd() { - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - if (!frameData_->hasInitCommands) { + // assert(insideFrame_ || firstFrame_); + + int curFrame = vulkan_->GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; + if (!frameData.hasInitCommands) { VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; begin.pInheritanceInfo = nullptr; - VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + VkResult res = vkBeginCommandBuffer(frameData.initCmd, &begin); + assert(res == VK_SUCCESS); + frameData.hasInitCommands = true; } return frameData_[vulkan_->GetCurFrame()].initCmd; } +// After flush. Should probably be part of it? void VulkanRenderManager::EndFrame() { + insideFrame_ = false; + FrameData &frame = frameData_[vulkan_->GetCurFrame()]; + TransitionToPresent(frame.mainCmd, swapchainImages_[current_buffer].image); VkResult res = vkEndCommandBuffer(frame.mainCmd); @@ -243,10 +258,13 @@ void VulkanRenderManager::EndFrame() { vkEndCommandBuffer(frame.initCmd); cmdBufs.push_back(frame.initCmd); frame.hasInitCommands = false; + ILOG("Frame %d had init commands", vulkan_->GetCurFrame()); } cmdBufs.push_back(frame.mainCmd); + vulkan_->EndFrame(); + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &acquireSemaphore_; @@ -259,74 +277,63 @@ void VulkanRenderManager::EndFrame() { res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frame.fence); assert(res == VK_SUCCESS); + VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; present.swapchainCount = 1; - VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); present.pSwapchains = &swapchain; present.pImageIndices = ¤t_buffer; present.pWaitSemaphores = &renderingCompleteSemaphore; present.waitSemaphoreCount = 1; present.pResults = nullptr; - res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI // return codes assert(res == VK_SUCCESS); - - vulkan_->EndFrame(); } void VulkanRenderManager::Sync() { } -void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb) { +void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { + VKRStep *step = new VKRStep{ VKRStepType::RENDER }; // This is what queues up new passes, and can end previous ones. + step->render.framebuffer = fb; + step->render.color = color; + step->render.depthStencil = depth; + step->render.clearColor = clearColor; + step->render.clearDepth = clearDepth; + step->render.clearStencil = clearStencil; + steps_.push_back(step); + curRenderStep_ = step; + curWidth_ = fb ? fb->width : vulkan_->GetBackbufferWidth(); + curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); } - -void VulkanRenderManager::BeginSurfaceRenderPass(VkCommandBuffer cmd, VkClearValue clear_value) { - FrameData &frame = frameData_[vulkan_->GetCurFrame()]; - VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - rp_begin.renderPass = backbufferRenderPass_; - rp_begin.framebuffer = framebuffers_[current_buffer]; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = curWidth_; - rp_begin.renderArea.extent.height = curHeight_; - rp_begin.clearValueCount = 1; - rp_begin.pClearValues = &clear_value; - vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); -} - -void VulkanRenderManager::EndSurfaceRenderPass(VkCommandBuffer cmd) { - // ILOG("VulkanContext::EndSurfaceRenderPass"); - vkCmdEndRenderPass(cmd); -} - -void VulkanRenderManager::InitFramebuffers() { +void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { VkResult U_ASSERT_ONLY res; - VkImageView attachments[1]; + // We share the same depth buffer but have multiple color buffers, see the loop below. + VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view }; - ILOG("InitFramebuffers: %dx%d", curWidth_, curHeight_); + ILOG("InitFramebuffers: %dx%d", width, height); VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; fb_info.renderPass = backbufferRenderPass_; - fb_info.attachmentCount = 1; + fb_info.attachmentCount = 2; fb_info.pAttachments = attachments; - fb_info.width = curWidth_; - fb_info.height = curHeight_; + fb_info.width = width; + fb_info.height = height; fb_info.layers = 1; - framebuffers_.resize(swapchainImageCount); + framebuffers_.resize(swapchainImageCount_); - for (uint32_t i = 0; i < swapchainImageCount; i++) { + for (uint32_t i = 0; i < swapchainImageCount_; i++) { attachments[0] = swapchainImages_[i].view; res = vkCreateFramebuffer(vulkan_->GetDevice(), &fb_info, nullptr, &framebuffers_[i]); assert(res == VK_SUCCESS); } } -void VulkanRenderManager::InitSurfaceRenderPass() { +void VulkanRenderManager::InitBackbufferRenderPass() { VkResult U_ASSERT_ONLY res; VkAttachmentDescription attachments[2]; @@ -340,6 +347,7 @@ void VulkanRenderManager::InitSurfaceRenderPass() { attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachments[0].flags = 0; + assert(depth_.format != VK_FORMAT_UNDEFINED); attachments[1].format = depth_.format; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; @@ -392,8 +400,8 @@ void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; image_info.imageType = VK_IMAGE_TYPE_2D; image_info.format = depth_format; - image_info.extent.width = curWidth_; - image_info.extent.height = curHeight_; + image_info.extent.width = vulkan_->GetBackbufferWidth(); + image_info.extent.height = vulkan_->GetBackbufferHeight(); image_info.extent.depth = 1; image_info.mipLevels = 1; image_info.arrayLayers = 1; @@ -509,64 +517,70 @@ void VulkanRenderManager::InitRenderpasses() { rp.pDependencies = nullptr; for (int depth = 0; depth < 3; depth++) { - switch ((RenderPassAction)depth) { - case RenderPassAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RenderPassAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RenderPassAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + switch ((VKRRenderPassAction)depth) { + case VKRRenderPassAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case VKRRenderPassAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case VKRRenderPassAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; } for (int color = 0; color < 3; color++) { - switch ((RenderPassAction)color) { - case RenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case RenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case RenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + switch ((VKRRenderPassAction)color) { + case VKRRenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case VKRRenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case VKRRenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; } - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((RenderPassAction)color, (RenderPassAction)depth)]); + vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((VKRRenderPassAction)color, (VKRRenderPassAction)depth)]); } } } -void VulkanRenderManager::BeginRenderPass() { - std::unique_lock lock(rpLock_); - - VKRStep *pass = new VKRStep(VKStepType::RENDER); - pass->stepType = VKStepType::RENDER; - steps_.push_back(pass); - curStep_ = steps_.back(); -} - void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); // If this is the first drawing command, merge it into the pass. - if (curStep_->render.numDraws == 0) { - curStep_->render.clearColor = clearColor; - curStep_->render.clearDepth = clearZ; - curStep_->render.clearStencil = clearStencil; - curStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? RenderPassAction::CLEAR : RenderPassAction::KEEP; - curStep_->render.depthStencil = (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) ? RenderPassAction::CLEAR : RenderPassAction::KEEP; + if (curRenderStep_->render.numDraws == 0) { + curRenderStep_->render.clearColor = clearColor; + curRenderStep_->render.clearDepth = clearZ; + curRenderStep_->render.clearStencil = clearStencil; + curRenderStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP; + curRenderStep_->render.depthStencil = (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP; } else { - VkRenderData data{ VkRenderCmd::CLEAR }; + VkRenderData data{ VKRRenderCommand::CLEAR }; data.clear.clearColor = clearColor; data.clear.clearZ = clearZ; data.clear.clearStencil = clearStencil; data.clear.clearMask = clearMask; - curStep_->commands.push_back(data); + curRenderStep_->commands.push_back(data); } } -void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint) { - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - EndCurrentRenderpass(frameData.mainCmd); +void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask) { + VKRStep *step = new VKRStep{ VKRStepType::COPY }; + step->copy.aspectMask = aspectMask; + step->copy.src = src; + step->copy.srcRect = srcRect; + step->copy.dst = dst; + step->copy.dstPos = dstPos; + std::unique_lock lock(mutex_); + steps_.push_back(step); + curRenderStep_ = nullptr; } -void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter) { - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - EndCurrentRenderpass(frameData.mainCmd); +void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { + VKRStep *step = new VKRStep{ VKRStepType::BLIT }; + step->blit.aspectMask = aspectMask; + step->blit.src = src; + step->blit.srcRect = srcRect; + step->blit.dst = dst; + step->blit.dstRect = dstRect; + step->blit.filter = filter; + std::unique_lock lock(mutex_); + steps_.push_back(step); + curRenderStep_ = nullptr; } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { // Should just mark the dependency and return the image. for (int i = 0; i < (int)steps_.size() - 1; i++) { - if (steps_[i]->stepType == VKStepType::RENDER && steps_[i]->render.framebuffer == fb) { + if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == fb) { if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; else @@ -598,37 +612,43 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Flush() { { - std::unique_lock lock(rpLock_); + std::unique_lock lock(mutex_); stepsOnThread_ = std::move(steps_); + curRenderStep_ = nullptr; } FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - VkCommandBuffer cmd = frameData.mainCmd; VkDevice device = vulkan_->GetDevice(); - // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. - // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, VK_NULL_HANDLE, ¤t_buffer); + VkCommandBuffer cmd = frameData.mainCmd; + + VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + begin.pInheritanceInfo = nullptr; + VkResult res = vkBeginCommandBuffer(cmd, &begin); + assert(res == VK_SUCCESS); + // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR // return codes + // TODO: Is it best to do this here, or combine with some other transition, or just do it right before the backbuffer bind-for-render? assert(res == VK_SUCCESS); - TransitionFromPresent(frameData.mainCmd, swapchainImages_[current_buffer].image); + TransitionFromPresent(cmd, swapchainImages_[current_buffer].image); // Optimizes renderpasses, then sequences them. for (int i = 0; i < stepsOnThread_.size(); i++) { const VKRStep &step = *stepsOnThread_[i]; switch (step.stepType) { - case VKStepType::RENDER: + case VKRStepType::RENDER: PerformRenderPass(step, cmd); break; - case VKStepType::COPY: + case VKRStepType::COPY: PerformCopy(step, cmd); break; - case VKStepType::BLIT: + case VKRStepType::BLIT: PerformBlit(step, cmd); break; - case VKStepType::READBACK: + case VKRStepType::READBACK: // PerformReadback break; } @@ -642,25 +662,25 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer auto &commands = step.commands; for (const auto &c : commands) { switch (c.cmd) { - case VkRenderCmd::VIEWPORT: + case VKRRenderCommand::VIEWPORT: vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp); break; - case VkRenderCmd::SCISSOR: + case VKRRenderCommand::SCISSOR: vkCmdSetScissor(cmd, 0, 1, &c.scissor.scissor); break; - case VkRenderCmd::BLEND: + case VKRRenderCommand::BLEND: vkCmdSetBlendConstants(cmd, c.blendColor.color); break; - case VkRenderCmd::STENCIL: + case VKRRenderCommand::STENCIL: vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); break; - case VkRenderCmd::DRAW_INDEXED: + case VKRRenderCommand::DRAW_INDEXED: vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); @@ -668,14 +688,14 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); break; - case VkRenderCmd::DRAW: + case VKRRenderCommand::DRAW: vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); vkCmdDraw(cmd, c.draw.count, 1, 0, 0); break; - case VkRenderCmd::CLEAR: + case VKRRenderCommand::CLEAR: { int numAttachments = 0; VkClearRect rc{}; @@ -735,21 +755,19 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st if (framebuf == curFramebuffer_) { if (framebuf == 0) Crash(); - if (!curRenderPass_) - Crash(); // If we're asking to clear, but already bound, we'll just keep it bound but send a clear command. // We will try to avoid this as much as possible. VkClearAttachment clear[2]{}; int count = 0; - if (step.render.color == RenderPassAction::CLEAR) { + if (step.render.color == VKRRenderPassAction::CLEAR) { clear[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; Uint8x4ToFloat4(clear[count].clearValue.color.float32, step.render.clearColor); clear[count].colorAttachment = 0; count++; } - if (step.render.depthStencil == RenderPassAction::CLEAR) { + if (step.render.depthStencil == VKRRenderPassAction::CLEAR) { clear[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; clear[count].clearValue.depthStencil.depth = step.render.clearDepth; clear[count].clearValue.depthStencil.stencil = step.render.clearStencil; @@ -765,13 +783,6 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st return; } - // OK, we're switching framebuffers. - if (curRenderPass_) { - vkCmdEndRenderPass(cmd); - curRenderPass_ = VK_NULL_HANDLE; - curFramebuffer_ = VK_NULL_HANDLE; - } - VkRenderPass renderPass; int numClearVals = 0; VkClearValue clearVal[2]; @@ -835,18 +846,20 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; // ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); - if (step.render.color == RenderPassAction::CLEAR) { + if (step.render.color == VKRRenderPassAction::CLEAR) { Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); numClearVals = 1; } - if (step.render.depthStencil == RenderPassAction::CLEAR) { + if (step.render.depthStencil == VKRRenderPassAction::CLEAR) { clearVal[1].depthStencil.depth = step.render.clearDepth; clearVal[1].depthStencil.stencil = step.render.clearStencil; numClearVals = 2; } } else { renderPass = GetSurfaceRenderPass(); - numClearVals = 2; + numClearVals = 2; // We don't bother with a depth buffer here. + clearVal[1].depthStencil.depth = 0.0f; + clearVal[1].depthStencil.stencil = 0; } VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; @@ -859,17 +872,9 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st rp_begin.clearValueCount = numClearVals; rp_begin.pClearValues = numClearVals ? clearVal : nullptr; vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); - curFramebuffer_ = framebuf; - curRenderPass_ = renderPass; curWidth_ = w; curHeight_ = h; -} - -void VulkanRenderManager::EndCurrentRenderpass(VkCommandBuffer cmd) { - if (curRenderPass_) { - vkCmdEndRenderPass(cmd); - curRenderPass_ = nullptr; - } + curFramebuffer_ = framebuf; } void VulkanRenderManager::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { @@ -1020,7 +1025,7 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) } } -void VulkanRenderManager::SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { +void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = img.layout; barrier.subresourceRange.layerCount = 1; @@ -1049,7 +1054,7 @@ void VulkanRenderManager::SetupTransitionToTransferSrc(VKImage &img, VkImageMemo img.layout = barrier.newLayout; } -void VulkanRenderManager::SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { +void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = img.layout; barrier.subresourceRange.layerCount = 1; diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h index fbc8f0976c..490fea3e1b 100644 --- a/Common/Vulkan/VulkanRenderManager.h +++ b/Common/Vulkan/VulkanRenderManager.h @@ -15,7 +15,7 @@ // The cool thing is that you can Flush on a different thread than you record the commands on! -enum class VkRenderCmd : uint8_t { +enum class VKRRenderCommand : uint8_t { STENCIL, BLEND, VIEWPORT, @@ -26,7 +26,7 @@ enum class VkRenderCmd : uint8_t { }; struct VkRenderData { - VkRenderCmd cmd; + VKRRenderCommand cmd; union { struct { VkPipeline pipeline; @@ -82,7 +82,7 @@ struct VkRenderData { }; }; -enum class VKStepType : uint8_t { +enum class VKRStepType : uint8_t { RENDER, COPY, BLIT, @@ -91,21 +91,21 @@ enum class VKStepType : uint8_t { class VKRFramebuffer; -enum class RenderPassAction { +enum class VKRRenderPassAction { DONT_CARE, CLEAR, KEEP, }; struct VKRStep { - VKRStep(VKStepType _type) : stepType(_type) {} - VKStepType stepType; + VKRStep(VKRStepType _type) : stepType(_type) {} + VKRStepType stepType; std::vector commands; union { struct { VKRFramebuffer *framebuffer; - RenderPassAction color; - RenderPassAction depthStencil; + VKRRenderPassAction color; + VKRRenderPassAction depthStencil; uint32_t clearColor; float clearDepth; int clearStencil; @@ -137,13 +137,13 @@ struct VKRStep { // Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine // to let them have individual non-pooled allocations. Until it's not fine. We'll see. -struct VKImage { +struct VKRImage { VkImage image; VkImageView imageView; VkDeviceMemory memory; VkImageLayout layout; }; -void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color); +void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color); class VKRFramebuffer { public: @@ -175,15 +175,14 @@ public: vulkan_->Delete().QueueDeleteImageView(depth.imageView); vulkan_->Delete().QueueDeleteDeviceMemory(color.memory); vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory); - vulkan_->Delete().QueueDeleteFramebuffer(framebuf); } int numShadows = 1; // TODO: Support this. VkFramebuffer framebuf = VK_NULL_HANDLE; - VKImage color{}; - VKImage depth{}; + VKRImage color{}; + VKRImage depth{}; int width = 0; int height = 0; @@ -199,43 +198,41 @@ public: void ThreadFunc(); // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. - void BeginFrameWrites(); + void BeginFrame(); void EndFrame(); - void BindFramebufferAsRenderTarget(VKRFramebuffer *fb); + void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); - void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPoint); - void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkFilter filter); - - void BeginRenderPass(); + void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask); + void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter); void SetViewport(const VkViewport &vp) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::VIEWPORT }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::VIEWPORT }; data.viewport.vp = vp; - curStep_->commands.push_back(data); + curRenderStep_->commands.push_back(data); } void SetScissor(const VkRect2D &rc) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::SCISSOR }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::SCISSOR }; data.scissor.scissor = rc; - curStep_->commands.push_back(data); + curRenderStep_->commands.push_back(data); } void SetBlendFactor(float color[4]) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::BLEND }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::BLEND }; CopyFloat4(data.blendColor.color, color); - curStep_->commands.push_back(data); + curRenderStep_->commands.push_back(data); } void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::DRAW }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::DRAW }; data.draw.count = count; data.draw.pipeline = pipeline; data.draw.pipelineLayout = layout; @@ -245,13 +242,13 @@ public: data.draw.numUboOffsets = numUboOffsets; for (int i = 0; i < numUboOffsets; i++) data.draw.uboOffsets[i] = uboOffsets[i]; - curStep_->commands.push_back(data); - curStep_->render.numDraws++; + curRenderStep_->commands.push_back(data); + curRenderStep_->render.numDraws++; } void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, VkIndexType indexType) { - _dbg_assert_(G3D, curStep_ && curStep_->stepType == VKStepType::RENDER); - VkRenderData data{ VkRenderCmd::DRAW_INDEXED }; + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::DRAW_INDEXED }; data.drawIndexed.count = count; data.drawIndexed.pipeline = pipeline; data.drawIndexed.pipelineLayout = layout; @@ -264,8 +261,8 @@ public: for (int i = 0; i < numUboOffsets; i++) data.drawIndexed.uboOffsets[i] = uboOffsets[i]; data.drawIndexed.indexType = indexType; - curStep_->commands.push_back(data); - curStep_->render.numDraws++; + curRenderStep_->commands.push_back(data); + curRenderStep_->render.numDraws++; } // Can run on a different thread! Just make sure to use BeginFrameWrites. @@ -274,12 +271,6 @@ public: // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); - std::vector steps_; - std::vector stepsOnThread_; - std::mutex rpLock_; - - VKRStep *curStep_; - VkCommandBuffer GetInitCmd(); VkCommandBuffer GetSurfaceCommandBuffer() { return frameData_[vulkan_->GetCurFrame()].mainCmd; @@ -291,33 +282,27 @@ public: return renderPasses_[i]; } + void CreateBackbuffers(); + void DestroyBackbuffers(); + private: - void InitFramebuffers(); - void InitSurfaceRenderPass(); + void InitBackbufferFramebuffers(int width, int height); + void InitBackbufferRenderPass(); void InitRenderpasses(); void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. - // The surface render pass is special because it has to acquire the backbuffer, and may thus "block". - // Use the returned command buffer to enqueue commands that render to the backbuffer. - // To render to other buffers first, you can submit additional commandbuffers using QueueBeforeSurfaceRender(cmd). - void BeginSurfaceRenderPass(VkCommandBuffer cmd, VkClearValue clear_value); - // May eventually need the ability to break and resume the backbuffer render pass in a few rare cases. - void EndSurfaceRenderPass(VkCommandBuffer cmd); - - void EndCurrentRenderpass(VkCommandBuffer cmd); - void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd); void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd); void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd); void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); - inline int RPIndex(RenderPassAction color, RenderPassAction depth) { + inline int RPIndex(VKRRenderPassAction color, VKRRenderPassAction depth) { return (int)depth * 3 + (int)color; } - static void SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - static void SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); VkSemaphore acquireSemaphore_; VkSemaphore renderingCompleteSemaphore; @@ -331,17 +316,21 @@ private: VkCommandPool cmdPool; VkCommandBuffer initCmd; VkCommandBuffer mainCmd; - bool hasInitCommands; + bool hasInitCommands = false; }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; VulkanContext *vulkan_; int curWidth_; int curHeight_; + bool insideFrame_ = false; std::thread submissionThread; std::mutex mutex_; std::condition_variable condVar_; + std::vector steps_; + std::vector stepsOnThread_; + VKRStep *curRenderStep_; struct SwapchainImageData { VkImage image; @@ -349,18 +338,17 @@ private: }; std::vector framebuffers_; std::vector swapchainImages_; - uint32_t swapchainImageCount; + uint32_t swapchainImageCount_; uint32_t current_buffer = 0; VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; struct DepthBufferInfo { - VkFormat format; + VkFormat format = VK_FORMAT_UNDEFINED; VkImage image = VK_NULL_HANDLE; VkDeviceMemory mem = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; }; DepthBufferInfo depth_; // Interpreter state - VkFramebuffer curFramebuffer_; - VkRenderPass curRenderPass_; - + VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; + // VkRenderPass curRenderPass_ = VK_NULL_HANDLE; }; diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index ef322d90e2..6f87a00cd7 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -128,7 +128,6 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep return false; if (msgCode == 64) // Another useless perf warning that will be seen less and less as we optimize - vkCmdClearAttachments() issued on command buffer object 0x00000195296C6D40 prior to any Draw Cmds. It is recommended you use RenderPass LOAD_OP_CLEAR on Attachments prior to any Draw. return false; - #ifdef _WIN32 std::string msg = message.str(); OutputDebugStringA(msg.c_str()); @@ -197,10 +196,13 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); bool success = draw_->CreatePresets(); assert(success); // Doesn't fail, we include the compiler. - return success; + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + return true; } void WindowsVulkanContext::Shutdown() { + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + delete draw_; draw_ = nullptr; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 2d409843cd..bb5e9dc64b 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -699,6 +699,8 @@ VKContext::VKContext(VulkanContext *vulkan) for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].pushBuffer = new VulkanPushBuffer(vulkan_, 1024 * 1024); + VkResult res = vkCreateDescriptorPool(device_, &dp, nullptr, &frame_[i].descriptorPool); + assert(res == VK_SUCCESS); } // binding 0 - uniform data @@ -746,7 +748,7 @@ VKContext::~VKContext() { } void VKContext::BeginFrame() { - renderManager_.BeginFrameWrites(); + renderManager_.BeginFrame(); FrameData &frame = frame_[frameNum_]; push_ = frame.pushBuffer; @@ -758,8 +760,6 @@ void VKContext::BeginFrame() { frame.descSets_.clear(); VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0); assert(result == VK_SUCCESS); - - SetScissorRect(0, 0, pixel_xres, pixel_yres); } void VKContext::WaitRenderCompletion(Framebuffer *fbo) { @@ -1146,7 +1146,7 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { VkDeviceSize offsets[1] = { vbBindOffset }; VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, vbBindOffset, vertexCount); + renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } // TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. @@ -1279,20 +1279,32 @@ void VKContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - renderManager_.CopyFramebuffer(src->GetFB(), VkRect2D{ {x, y}, {(uint32_t)width, (uint32_t)height } }, dst->GetFB(), VkOffset2D{ dstX, dstY }); + int aspectMask = 0; + if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + + renderManager_.CopyFramebuffer(src->GetFB(), VkRect2D{ {x, y}, {(uint32_t)width, (uint32_t)height } }, dst->GetFB(), VkOffset2D{ dstX, dstY }, aspectMask); } bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) { VKFramebuffer *src = (VKFramebuffer *)srcfb; VKFramebuffer *dst = (VKFramebuffer *)dstfb; - renderManager_.BlitFramebuffer(src->GetFB(), VkRect2D{ {srcX1, srcY1}, {(uint32_t)(srcX2 - srcX1), (uint32_t)(srcY2 - srcY1) } }, dst->GetFB(), VkRect2D{ {dstX1, dstY1}, {(uint32_t)(dstX2 - dstX1), (uint32_t)(dstY2 - dstY1) } }, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); + int aspectMask = 0; + if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + + renderManager_.BlitFramebuffer(src->GetFB(), VkRect2D{ {srcX1, srcY1}, {(uint32_t)(srcX2 - srcX1), (uint32_t)(srcY2 - srcY1) } }, dst->GetFB(), VkRect2D{ {dstX1, dstY1}, {(uint32_t)(dstX2 - dstX1), (uint32_t)(dstY2 - dstY1) } }, aspectMask, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); return true; } void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { VKFramebuffer *fb = (VKFramebuffer *)fbo; - renderManager_.BindFramebufferAsRenderTarget(fb->GetFB()); + VKRRenderPassAction color = (VKRRenderPassAction)rp.color; // same values. + VKRRenderPassAction depth = (VKRRenderPassAction)rp.color; // same values. + renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); } // color must be 0, for now. @@ -1324,8 +1336,10 @@ void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void *param2) { switch (ev) { case Event::LOST_BACKBUFFER: + renderManager_.DestroyBackbuffers(); break; case Event::GOT_BACKBUFFER: + renderManager_.CreateBackbuffers(); break; } // Noop From 02f76ae4a8851aa1a1af57c533704dc5260bff86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 13:25:45 +0200 Subject: [PATCH 054/111] Rendering basics now works. --- Common/Common.vcxproj | 2 + Common/Common.vcxproj.filters | 16 +- Common/Vulkan/VulkanQueueRunner.cpp | 0 Common/Vulkan/VulkanQueueRunner.h | 6 + Common/Vulkan/VulkanRenderManager.cpp | 208 ++++++++++++++++++-------- Common/Vulkan/VulkanRenderManager.h | 53 +++++-- GPU/Common/FramebufferCommon.cpp | 2 +- GPU/Vulkan/DrawEngineVulkan.cpp | 70 ++++----- GPU/Vulkan/DrawEngineVulkan.h | 9 +- GPU/Vulkan/FramebufferVulkan.cpp | 4 +- GPU/Vulkan/StateMappingVulkan.cpp | 22 +-- GPU/Vulkan/StateMappingVulkan.h | 5 +- ext/native/thin3d/thin3d.h | 4 +- ext/native/thin3d/thin3d_vulkan.cpp | 28 ++-- 14 files changed, 257 insertions(+), 172 deletions(-) create mode 100644 Common/Vulkan/VulkanQueueRunner.cpp create mode 100644 Common/Vulkan/VulkanQueueRunner.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 58a8fac99a..07fc66510c 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -249,6 +249,7 @@ + @@ -321,6 +322,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index c3ae5eea9e..413f08cb8b 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -75,7 +75,12 @@ - + + Vulkan + + + Vulkan + @@ -139,7 +144,12 @@ - + + Vulkan + + + Vulkan + @@ -155,4 +165,4 @@ {c14d66ef-5f7c-4565-975a-72774e7ccfb9} - + \ No newline at end of file diff --git a/Common/Vulkan/VulkanQueueRunner.cpp b/Common/Vulkan/VulkanQueueRunner.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Common/Vulkan/VulkanQueueRunner.h b/Common/Vulkan/VulkanQueueRunner.h new file mode 100644 index 0000000000..9da875a95c --- /dev/null +++ b/Common/Vulkan/VulkanQueueRunner.h @@ -0,0 +1,6 @@ +#pragma once + +class VulkanQueueRunner { +public: + +}; \ No newline at end of file diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/Common/Vulkan/VulkanRenderManager.cpp index eab92174aa..a2e2d7fd1f 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/Common/Vulkan/VulkanRenderManager.cpp @@ -141,13 +141,12 @@ void VulkanRenderManager::CreateBackbuffers() { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - res = vkCreateImageView(vulkan_->GetDevice(), - &color_image_view, NULL, &sc_buffer.view); + res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, NULL, &sc_buffer.view); swapchainImages_.push_back(sc_buffer); assert(res == VK_SUCCESS); } delete[] swapchainImages; - current_buffer = -1; + curSwapchainImage_ = -1; InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. InitBackbufferRenderPass(); // Must be before InitFramebuffers. @@ -186,7 +185,6 @@ VulkanRenderManager::~VulkanRenderManager() { } framebuffers_.clear(); for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { - // vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]); vkDestroyRenderPass(device, renderPasses_[i], nullptr); } } @@ -201,14 +199,13 @@ void VulkanRenderManager::ThreadFunc() { } void VulkanRenderManager::BeginFrame() { - VkDevice device = vulkan_->GetDevice(); FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, ¤t_buffer); + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage_); assert(res == VK_SUCCESS); // Make sure the very last command buffer from the frame before the previous has been fully executed. @@ -243,7 +240,7 @@ void VulkanRenderManager::EndFrame() { FrameData &frame = frameData_[vulkan_->GetCurFrame()]; - TransitionToPresent(frame.mainCmd, swapchainImages_[current_buffer].image); + TransitionToPresent(frame.mainCmd, swapchainImages_[curSwapchainImage_].image); VkResult res = vkEndCommandBuffer(frame.mainCmd); assert(res == VK_SUCCESS); @@ -281,7 +278,7 @@ void VulkanRenderManager::EndFrame() { VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; present.swapchainCount = 1; present.pSwapchains = &swapchain; - present.pImageIndices = ¤t_buffer; + present.pImageIndices = &curSwapchainImage_; present.pWaitSemaphores = &renderingCompleteSemaphore; present.waitSemaphoreCount = 1; present.pResults = nullptr; @@ -296,6 +293,14 @@ void VulkanRenderManager::Sync() { } void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { + // Eliminate dupes. + if (steps_.size() && steps_.back()->stepType == VKRStepType::RENDER && steps_.back()->render.framebuffer == fb) { + if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR) { + // We don't move to a new step, this bind was unnecessary. + return; + } + } + VKRStep *step = new VKRStep{ VKRStepType::RENDER }; // This is what queues up new passes, and can end previous ones. step->render.framebuffer = fb; @@ -304,7 +309,10 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR step->render.clearColor = clearColor; step->render.clearDepth = clearDepth; step->render.clearStencil = clearStencil; + step->render.numDraws = 0; + step->render.finalColorLayout = !fb ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; steps_.push_back(step); + curRenderStep_ = step; curWidth_ = fb ? fb->width : vulkan_->GetBackbufferWidth(); curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); @@ -579,34 +587,20 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { // Should just mark the dependency and return the image. - for (int i = 0; i < (int)steps_.size() - 1; i++) { + for (int i = (int)steps_.size() - 1; i >= 0; i--) { if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == fb) { - if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) + // If this framebuffer was rendered to earlier in this frame, make sure to pre-transition it to the correct layout. + if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) { steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - else - Crash(); // May need to shadow the framebuffer? + break; + } + else { + // May need to shadow the framebuffer if we re-order passes later. + } } } - /* - VkAccessFlags srcAccessMask; - VkPipelineStageFlags srcStage; - switch (fb->color.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - } - TransitionImageLayout2(transitionCmdBuf, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, - fb->color.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - srcAccessMask, VK_ACCESS_SHADER_READ_BIT); - fb->color.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - */ + curRenderStep_->preTransitions.push_back({ fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }); return fb->color.imageView; } @@ -633,7 +627,7 @@ void VulkanRenderManager::Flush() { // return codes // TODO: Is it best to do this here, or combine with some other transition, or just do it right before the backbuffer bind-for-render? assert(res == VK_SUCCESS); - TransitionFromPresent(cmd, swapchainImages_[current_buffer].image); + TransitionFromPresent(cmd, swapchainImages_[curSwapchainImage_].image); // Optimizes renderpasses, then sequences them. for (int i = 0; i < stepsOnThread_.size(); i++) { @@ -658,8 +652,60 @@ void VulkanRenderManager::Flush() { } void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { + // TODO: If there are multiple, we can transition them together. + for (const auto &iter : step.preTransitions) { + if (iter.fb->color.layout != iter.targetLayout) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = iter.fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = iter.fb->color.image; + barrier.srcAccessMask = 0; + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; + switch (barrier.oldLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + default: + Crash(); + break; + } + barrier.newLayout = iter.targetLayout; + switch (barrier.newLayout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + default: + Crash(); + break; + } + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier(cmd, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + iter.fb->color.layout = barrier.newLayout; + } + } + PerformBindFramebufferAsRenderTarget(step, cmd); + + VKRFramebuffer *fb = step.render.framebuffer; + auto &commands = step.commands; + + // TODO: Dynamic state commands (SetViewport, SetScissor, SetBlendConstants, SetStencil*) are only + // valid when a pipeline is bound with those as dynamic state. So we need to add some state tracking here + // for this to be correct. This is a bit of a pain but also will let us eliminate redundant calls. + for (const auto &c : commands) { switch (c.cmd) { case VKRRenderCommand::VIEWPORT: @@ -733,6 +779,41 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer } } vkCmdEndRenderPass(cmd); + + // Transition the framebuffer if requested. + if (fb && step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = fb->color.image; + barrier.srcAccessMask = 0; + switch (barrier.oldLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + default: + Crash(); + } + barrier.newLayout = step.render.finalColorLayout; + switch (barrier.newLayout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + default: + Crash(); + } + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + + // we're between passes so it's OK. + // ARM Best Practices guide recommends these stage bits. + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + fb->color.layout = barrier.newLayout; + } } void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { @@ -747,7 +828,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st h = fb->height; prevLayout = fb->color.layout; } else { - framebuf = framebuffers_[current_buffer]; + framebuf = framebuffers_[curSwapchainImage_]; w = vulkan_->GetBackbufferWidth(); h = vulkan_->GetBackbufferHeight(); } @@ -792,56 +873,51 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st // Now, if the image needs transitioning, let's transition. // The backbuffer does not, that's handled by VulkanContext. if (step.render.framebuffer->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->color.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->color.image; - barrier.srcAccessMask = 0; + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; switch (fb->color.layout) { case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; } - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - // TODO: Double-check these flags. Should be fine. - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->color.layout = barrier.newLayout; + + TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT); + fb->color.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->depth.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->depth.image; - barrier.srcAccessMask = 0; + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; switch (fb->depth.layout) { case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; } - barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - // TODO: Double-check these flags. Should be fine. - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->depth.layout = barrier.newLayout; + TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT); + fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; @@ -856,7 +932,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st numClearVals = 2; } } else { - renderPass = GetSurfaceRenderPass(); + renderPass = GetBackbufferRenderpass(); numClearVals = 2; // We don't bother with a depth buffer here. clearVal[1].depthStencil.depth = 0.0f; clearVal[1].depthStencil.stencil = 0; diff --git a/Common/Vulkan/VulkanRenderManager.h b/Common/Vulkan/VulkanRenderManager.h index 490fea3e1b..e46072a223 100644 --- a/Common/Vulkan/VulkanRenderManager.h +++ b/Common/Vulkan/VulkanRenderManager.h @@ -97,10 +97,16 @@ enum class VKRRenderPassAction { KEEP, }; +struct TransitionRequest { + VKRFramebuffer *fb; + VkImageLayout targetLayout; +}; + struct VKRStep { VKRStep(VKRStepType _type) : stepType(_type) {} VKRStepType stepType; std::vector commands; + std::vector preTransitions; union { struct { VKRFramebuffer *framebuffer; @@ -221,6 +227,15 @@ public: curRenderStep_->commands.push_back(data); } + void SetStencilParams(uint8_t writeMask, uint8_t compareMask, uint8_t refValue) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::STENCIL }; + data.stencil.stencilWriteMask = writeMask; + data.stencil.stencilCompareMask = compareMask; + data.stencil.stencilRef = refValue; + curRenderStep_->commands.push_back(data); + } + void SetBlendFactor(float color[4]) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::BLEND }; @@ -230,7 +245,7 @@ public: void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); - void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { + void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::DRAW }; data.draw.count = count; @@ -246,10 +261,11 @@ public: curRenderStep_->render.numDraws++; } - void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, VkIndexType indexType) { + void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::DRAW_INDEXED }; data.drawIndexed.count = count; + data.drawIndexed.instances = numInstances; data.drawIndexed.pipeline = pipeline; data.drawIndexed.pipelineLayout = layout; data.drawIndexed.ds = descSet; @@ -272,15 +288,19 @@ public: void Sync(); VkCommandBuffer GetInitCmd(); - VkCommandBuffer GetSurfaceCommandBuffer() { - return frameData_[vulkan_->GetCurFrame()].mainCmd; - } - VkRenderPass GetSurfaceRenderPass() const { + VkRenderPass GetBackbufferRenderpass() const { return backbufferRenderPass_; } VkRenderPass GetRenderPass(int i) const { return renderPasses_[i]; } + VkRenderPass GetCompatibleRenderpass() const { + if (curRenderStep_ && curRenderStep_->render.framebuffer != nullptr) { + return GetRenderPass(0); + } else { + return backbufferRenderPass_; + } + } void CreateBackbuffers(); void DestroyBackbuffers(); @@ -304,13 +324,15 @@ private: static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + // Permanent objects VkSemaphore acquireSemaphore_; VkSemaphore renderingCompleteSemaphore; - + VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. // TODO: Create these on demand. VkRenderPass renderPasses_[9]; + // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { VkFence fence; VkCommandPool cmdPool; @@ -320,18 +342,23 @@ private: }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; - VulkanContext *vulkan_; + // Submission time state int curWidth_; int curHeight_; bool insideFrame_ = false; + VKRStep *curRenderStep_; + VKRFramebuffer *boundFramebuffer_; + std::vector steps_; + // Execution time state + VulkanContext *vulkan_; std::thread submissionThread; std::mutex mutex_; std::condition_variable condVar_; - std::vector steps_; std::vector stepsOnThread_; - VKRStep *curRenderStep_; + VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; + // Swap chain management struct SwapchainImageData { VkImage image; VkImageView view; @@ -339,8 +366,7 @@ private: std::vector framebuffers_; std::vector swapchainImages_; uint32_t swapchainImageCount_; - uint32_t current_buffer = 0; - VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; + uint32_t curSwapchainImage_ = 0; struct DepthBufferInfo { VkFormat format = VK_FORMAT_UNDEFINED; VkImage image = VK_NULL_HANDLE; @@ -348,7 +374,4 @@ private: VkImageView view = VK_NULL_HANDLE; }; DepthBufferInfo depth_; - // Interpreter state - VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; - // VkRenderPass curRenderPass_ = VK_NULL_HANDLE; }; diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 266d48a480..c10f7ac653 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -809,7 +809,7 @@ void FramebufferManagerCommon::SetViewport2D(int x, int y, int w, int h) { } void FramebufferManagerCommon::CopyDisplayToOutput() { - DownloadFramebufferOnSwitch(currentRenderVfb_); + // DownloadFramebufferOnSwitch(currentRenderVfb_); currentRenderVfb_ = 0; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 896067b97e..692ce13ec2 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -23,6 +23,7 @@ #include "profiler/profiler.h" #include "Common/MemoryUtil.h" +#include "Common/Vulkan/VulkanRenderManager.h" #include "Core/MemMap.h" #include "Core/Host.h" #include "Core/System.h" @@ -275,7 +276,6 @@ void DrawEngineVulkan::DeviceRestore(VulkanContext *vulkan) { } void DrawEngineVulkan::BeginFrame() { - lastCmd_ = VK_NULL_HANDLE; lastPipeline_ = nullptr; FrameData *frame = &frame_[curFrame_]; @@ -644,19 +644,12 @@ void DrawEngineVulkan::DoFlush() { // TODO: Should be enough to update this once per frame? gpuStats.numTrackedVertexArrays = (int)vai_.size(); - /* - VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER); - if (cmd != lastCmd_) { - lastPipeline_ = nullptr; - lastCmd_ = cmd; - // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); - }*/ - VkCommandBuffer cmd = VK_NULL_HANDLE; - - VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::CURRENT_RENDERPASS); - if (!rp) - Crash(); + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + + // HACK: These two lines should only execute if we started on a new render pass. + lastPipeline_ = nullptr; + // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. + // gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); FrameData *frame = &frame_[curFrame_]; @@ -873,26 +866,28 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } - if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { + VulkanPipeline *pipeline = lastPipeline_; + if (!lastPipeline_ || !gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); + Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; + VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); + pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); if (!pipeline) { // Already logged, let's bail out. return; } if (pipeline != lastPipeline_) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { gstate_c.Dirty(DIRTY_BLEND_STATE); } lastPipeline_ = pipeline; } - ApplyDrawStateLate(cmd, false, 0, pipeline->useBlendConstant); + ApplyDrawStateLate(renderManager, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + lastPipeline_ = pipeline; } lastPrim_ = prim; @@ -902,28 +897,22 @@ void DrawEngineVulkan::DoFlush() { VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf); { - PROFILE_THIS_SCOPE("vkdraw"); + PROFILE_THIS_SCOPE("renderman_q"); const uint32_t dynamicUBOOffsets[3] = { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets); + // vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets); int stride = dec_->GetDecVtxFmt().stride; - VkDeviceSize offsets[1] = { vbOffset }; if (useElements) { if (!ibuf) ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); - // TODO (maybe): Avoid rebinding vertex/index buffers if the vertex size stays the same by using the offset arguments. - // Not sure if actually worth it, binding buffers should be fast. - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets); - vkCmdBindIndexBuffer(cmd, ibuf, ibOffset, VK_INDEX_TYPE_UINT16); int numInstances = (gstate_c.bezier || gstate_c.spline) ? numPatches : 1; - vkCmdDrawIndexed(cmd, vertexCount, numInstances, 0, 0, 0); + renderManager->DrawIndexed(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, numInstances, VK_INDEX_TYPE_UINT16); } else { - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets); - vkCmdDraw(cmd, vertexCount, 1, 0, 0); + renderManager->Draw(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, vertexCount); } } } else { @@ -977,27 +966,28 @@ void DrawEngineVulkan::DoFlush() { if (sampler == VK_NULL_HANDLE) sampler = nullSampler_; } - + VulkanPipeline *pipeline = lastPipeline_; if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); + Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; + VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); + pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); if (!pipeline) { // Already logged, let's bail out. return; } if (pipeline != lastPipeline_) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw. if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { gstate_c.Dirty(DIRTY_BLEND_STATE); } lastPipeline_ = pipeline; } - ApplyDrawStateLate(cmd, false, 0, pipeline->useBlendConstant); + ApplyDrawStateLate(renderManager, false, 0, pipeline->useBlendConstant); gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + lastPipeline_ = pipeline; } lastPrim_ = prim; @@ -1011,25 +1001,19 @@ void DrawEngineVulkan::DoFlush() { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; - PROFILE_THIS_SCOPE("vkdrawsoft"); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets); + PROFILE_THIS_SCOPE("renderman_q"); if (drawIndexed) { VkBuffer vbuf, ibuf; vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); ibOffset = (uint32_t)frame->pushIndex->Push(inds, sizeof(short) * numTrans, &ibuf); VkDeviceSize offsets[1] = { vbOffset }; - // TODO: Avoid rebinding if the vertex size stays the same by using the offset arguments - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets); - vkCmdBindIndexBuffer(cmd, ibuf, ibOffset, VK_INDEX_TYPE_UINT16); - vkCmdDrawIndexed(cmd, numTrans, 1, 0, 0, 0); + renderManager->DrawIndexed(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, numTrans, 1, VK_INDEX_TYPE_UINT16); } else { VkBuffer vbuf; vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vbuf); VkDeviceSize offsets[1] = { vbOffset }; - // TODO: Avoid rebinding if the vertex size stays the same by using the offset arguments - vkCmdBindVertexBuffers(cmd, 0, 1, &vbuf, offsets); - vkCmdDraw(cmd, numTrans, 1, 0, 0); + renderManager->Draw(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, numTrans); } } else if (result.action == SW_CLEAR) { // Note: we won't get here if the clear is alpha but not color, or color but not alpha. diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 098a16a5f6..5ecd84ad61 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -113,6 +113,8 @@ public: u8 flags = 0; }; +class VulkanRenderManager; + // Handles transform, lighting and drawing. class DrawEngineVulkan : public DrawEngineCommon { public: @@ -161,6 +163,10 @@ public: void DirtyAllUBOs(); + void DirtyPipeline() { + lastPipeline_ = nullptr; + } + VulkanPushBuffer *GetPushBufferForTextureData() { return frame_[curFrame_].pushUBO; } @@ -171,7 +177,7 @@ public: private: struct FrameData; - void ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); + void ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); void InitDeviceObjects(); @@ -191,7 +197,6 @@ private: // We use a single descriptor set layout for all PSP draws. VkDescriptorSetLayout descriptorSetLayout_; VkPipelineLayout pipelineLayout_; - VkCommandBuffer lastCmd_ = VK_NULL_HANDLE; VulkanPipeline *lastPipeline_; VkDescriptorSet lastDs_ = VK_NULL_HANDLE; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 96e08b8923..3422f8a117 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -132,8 +132,8 @@ void FramebufferManagerVulkan::InitDeviceObjects() { assert(vsBasicTex_ != VK_NULL_HANDLE); // Prime the 2D pipeline cache. - vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); - vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS), vsBasicTex_, fsBasicTex_); + // vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); + // vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index dc390ef2af..3e919eec92 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/Vulkan/VulkanLoader.h" +#include "Common/Vulkan/VulkanRenderManager.h" #include "math/dataconv.h" #include "GPU/Math3D.h" @@ -369,7 +370,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag } } -void DrawEngineVulkan::ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant) { +void DrawEngineVulkan::ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant) { // At this point, we know if the vertices are full alpha or not. // TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? if (!gstate.isModeClear()) { @@ -386,24 +387,15 @@ void DrawEngineVulkan::ApplyDrawStateLate(VkCommandBuffer cmd, bool applyStencil } if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) { - vkCmdSetScissor(cmd, 0, 1, &dynState_.scissor); - vkCmdSetViewport(cmd, 0, 1, &dynState_.viewport); + renderManager->SetScissor(dynState_.scissor); + renderManager->SetViewport(dynState_.viewport); } - if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { - if (dynState_.useStencil) { - vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilWriteMask); - vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilCompareMask); - if (!applyStencilRef) { - vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, dynState_.stencilRef); - } - } - } - if (applyStencilRef) { - vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, stencilRef); + if ((gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE) && dynState_.useStencil) || applyStencilRef) { + renderManager->SetStencilParams(dynState_.stencilWriteMask, dynState_.stencilCompareMask, applyStencilRef ? stencilRef : dynState_.stencilRef); } if (gstate_c.IsDirty(DIRTY_BLEND_STATE) && useBlendConstant) { float bc[4]; Uint8x4ToFloat4(bc, dynState_.blendColor); - vkCmdSetBlendConstants(cmd, bc); + renderManager->SetBlendFactor(bc); } } diff --git a/GPU/Vulkan/StateMappingVulkan.h b/GPU/Vulkan/StateMappingVulkan.h index e4b67a3b57..f1d8013f69 100644 --- a/GPU/Vulkan/StateMappingVulkan.h +++ b/GPU/Vulkan/StateMappingVulkan.h @@ -57,7 +57,4 @@ struct VulkanPipelineRasterStateKey { size_t size = sizeof(VulkanPipelineRasterStateKey); return memcmp(this, &other, size) < 0; } -}; - -class ShaderManagerVulkan; -void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); +}; \ No newline at end of file diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 5e18ad9927..fdb68a3849 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -321,9 +321,9 @@ enum class NativeObject { BACKBUFFER_COLOR_TEX, BACKBUFFER_DEPTH_TEX, FEATURE_LEVEL, - BACKBUFFER_RENDERPASS, COMPATIBLE_RENDERPASS, - CURRENT_RENDERPASS, + BACKBUFFER_RENDERPASS, + FRAMEBUFFER_RENDERPASS, INIT_COMMANDBUFFER, BOUND_TEXTURE_IMAGEVIEW, RENDER_MANAGER, diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index bb5e9dc64b..967b27c85e 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -452,16 +452,13 @@ public: uintptr_t GetNativeObject(NativeObject obj) override { switch (obj) { - case NativeObject::COMPATIBLE_RENDERPASS: + case NativeObject::FRAMEBUFFER_RENDERPASS: // Return a representative renderpass. - if (curRenderPass_ == renderManager_.GetSurfaceRenderPass()) - return (uintptr_t)curRenderPass_; - else - return (uintptr_t)renderManager_.GetRenderPass(0); + return (uintptr_t)renderManager_.GetRenderPass(0); case NativeObject::BACKBUFFER_RENDERPASS: - return (uintptr_t)renderManager_.GetSurfaceRenderPass(); - case NativeObject::CURRENT_RENDERPASS: - return (uintptr_t)curRenderPass_; + return (uintptr_t)renderManager_.GetBackbufferRenderpass(); + case NativeObject::COMPATIBLE_RENDERPASS: + return (uintptr_t)renderManager_.GetCompatibleRenderpass(); case NativeObject::INIT_COMMANDBUFFER: return (uintptr_t)renderManager_.GetInitCmd(); case NativeObject::BOUND_TEXTURE_IMAGEVIEW: @@ -470,6 +467,7 @@ public: case NativeObject::RENDER_MANAGER: return (uintptr_t)&renderManager_; default: + Crash(); return 0; } } @@ -521,9 +519,6 @@ private: VulkanPushBuffer *push_ = nullptr; DeviceCaps caps_{}; - - VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; - VkRenderPass curRenderPass_ = VK_NULL_HANDLE; }; static int GetBpp(VkFormat format) { @@ -907,7 +902,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically. info.layout = pipelineLayout_; info.subpass = 0; - info.renderPass = renderManager_.GetSurfaceRenderPass(); + info.renderPass = renderManager_.GetBackbufferRenderpass(); // OK, need to create a new pipeline. VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 1, &info, nullptr, &pipeline->vkpipeline); @@ -1134,7 +1129,7 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.DrawIndexed(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, VK_INDEX_TYPE_UINT32); + renderManager_.DrawIndexed(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, 1, VK_INDEX_TYPE_UINT32); } void VKContext::DrawUP(const void *vdata, int vertexCount) { @@ -1151,11 +1146,6 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { // TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) { - if (!curRenderPass_) { - ELOG("Clear: Need an active render pass"); - return; - } - int mask = 0; if (clearMask & FBChannel::FB_COLOR_BIT) mask |= VK_IMAGE_ASPECT_COLOR_BIT; @@ -1315,7 +1305,7 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne if (channelBit & FBChannel::FB_COLOR_BIT) aspect |= VK_IMAGE_ASPECT_COLOR_BIT; if (channelBit & FBChannel::FB_DEPTH_BIT) aspect |= VK_IMAGE_ASPECT_DEPTH_BIT; if (channelBit & FBChannel::FB_STENCIL_BIT) aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; - renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); + boundImageView_[0] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); } uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { From 1c5bd0f8dcea934806906430066888055a135183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 16:28:35 +0200 Subject: [PATCH 055/111] Move the VulkanRenderManager stuff to thin3d, fits in better there. --- CMakeLists.txt | 5 ++++- Common/Common.vcxproj | 4 ---- Common/Common.vcxproj.filters | 12 ------------ GPU/Vulkan/DrawEngineVulkan.cpp | 4 ++-- GPU/Vulkan/FramebufferVulkan.cpp | 2 +- GPU/Vulkan/StateMappingVulkan.cpp | 2 +- android/jni/app-android.cpp | 9 ++++++--- ext/native/native.vcxproj | 4 ++++ ext/native/native.vcxproj.filters | 12 ++++++++++++ ext/native/profiler/profiler.h | 3 ++- .../native/thin3d}/VulkanQueueRunner.cpp | 0 .../Vulkan => ext/native/thin3d}/VulkanQueueRunner.h | 0 .../native/thin3d}/VulkanRenderManager.cpp | 4 ++-- .../native/thin3d}/VulkanRenderManager.h | 0 ext/native/thin3d/thin3d_vulkan.cpp | 2 +- 15 files changed, 35 insertions(+), 28 deletions(-) rename {Common/Vulkan => ext/native/thin3d}/VulkanQueueRunner.cpp (100%) rename {Common/Vulkan => ext/native/thin3d}/VulkanQueueRunner.h (100%) rename {Common/Vulkan => ext/native/thin3d}/VulkanRenderManager.cpp (99%) rename {Common/Vulkan => ext/native/thin3d}/VulkanRenderManager.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8cdbdff811..a3fc60adde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -803,7 +803,10 @@ endif() set(THIN3D_PLATFORMS ext/native/thin3d/thin3d_gl.cpp) if(VULKAN) - set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} ext/native/thin3d/thin3d_vulkan.cpp) + set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} + ext/native/thin3d/thin3d_vulkan.cpp + ext/native/thin3d/VulkanRenderManager.cpp + ext/native/thin3d/VulkanRenderManager.h) endif() if(WIN32) set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 07fc66510c..86758c4229 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -249,8 +249,6 @@ - - @@ -322,8 +320,6 @@ - - diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 413f08cb8b..0251f2b334 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -75,12 +75,6 @@ - - Vulkan - - - Vulkan - @@ -144,12 +138,6 @@ - - Vulkan - - - Vulkan - diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 692ce13ec2..0cc249cf9a 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -21,9 +21,9 @@ #include "base/timeutil.h" #include "math/dataconv.h" #include "profiler/profiler.h" +#include "thin3d/VulkanRenderManager.h" #include "Common/MemoryUtil.h" -#include "Common/Vulkan/VulkanRenderManager.h" #include "Core/MemMap.h" #include "Core/Host.h" #include "Core/System.h" @@ -646,7 +646,7 @@ void DrawEngineVulkan::DoFlush() { VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - // HACK: These two lines should only execute if we started on a new render pass. + // HACK: These two lines should only execute if we started on a new render pass. Can't tell from in here though... lastPipeline_ = nullptr; // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. // gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 3422f8a117..bf33417a4a 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -28,7 +28,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanMemory.h" #include "Common/Vulkan/VulkanImage.h" -#include "Common/Vulkan/VulkanRenderManager.h" +#include "thin3d/VulkanRenderManager.h" #include "Common/ColorConv.h" #include "Core/Host.h" #include "Core/MemMap.h" diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 3e919eec92..3c170cc784 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -16,7 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/Vulkan/VulkanLoader.h" -#include "Common/Vulkan/VulkanRenderManager.h" +#include "thin3d/VulkanRenderManager.h" #include "math/dataconv.h" #include "GPU/Math3D.h" diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 5ed28dbd46..e0e772d1c1 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -312,19 +312,20 @@ bool AndroidVulkanContext::Init(ANativeWindow *wnd, int desiredBackbufferSizeX, int bits = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; g_Vulkan->InitDebugMsgCallback(&Vulkan_Dbg, bits, &g_LogOptions); } - g_Vulkan->InitObjects(true); + g_Vulkan->InitObjects(); draw_ = Draw::T3DCreateVulkanContext(g_Vulkan); bool success = draw_->CreatePresets(); // Doesn't fail, we ship the compiler. assert(success); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); return true; } void AndroidVulkanContext::Shutdown() { ILOG("AndroidVulkanContext::Shutdown"); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); delete draw_; draw_ = nullptr; NativeShutdownGraphics(); - g_Vulkan->WaitUntilQueueIdle(); g_Vulkan->DestroyObjects(); g_Vulkan->DestroyDebugMsgCallback(); @@ -341,11 +342,13 @@ void AndroidVulkanContext::SwapBuffers() { void AndroidVulkanContext::Resize() { g_Vulkan->WaitUntilQueueIdle(); + draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); g_Vulkan->DestroyObjects(); // backbufferResize updated these values. TODO: Notify another way? g_Vulkan->ReinitSurfaceAndroid(pixel_xres, pixel_yres); - g_Vulkan->InitObjects(g_Vulkan); + g_Vulkan->InitObjects(); + draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } void AndroidVulkanContext::SwapInterval(int interval) { diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index ea47f94dea..681a99b453 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -240,6 +240,8 @@ + + @@ -697,6 +699,8 @@ + + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index e551ed8b0e..57aee62de4 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -320,6 +320,12 @@ gfx + + thin3d + + + thin3d + @@ -781,6 +787,12 @@ math + + thin3d + + + thin3d + diff --git a/ext/native/profiler/profiler.h b/ext/native/profiler/profiler.h index e4cd55d255..f615e2e88c 100644 --- a/ext/native/profiler/profiler.h +++ b/ext/native/profiler/profiler.h @@ -2,6 +2,8 @@ #include +// NOTE : This profiler is very single-threaded. Cannot be used from multiple threads yet. + // #define USE_PROFILER #ifdef USE_PROFILER @@ -14,7 +16,6 @@ void internal_profiler_end_frame(); int internal_profiler_enter(const char *category_name); // Returns the category number. void internal_profiler_leave(int category); - const char *Profiler_GetCategoryName(int i); int Profiler_GetNumCategories(); int Profiler_GetHistoryLength(); diff --git a/Common/Vulkan/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp similarity index 100% rename from Common/Vulkan/VulkanQueueRunner.cpp rename to ext/native/thin3d/VulkanQueueRunner.cpp diff --git a/Common/Vulkan/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h similarity index 100% rename from Common/Vulkan/VulkanQueueRunner.h rename to ext/native/thin3d/VulkanQueueRunner.h diff --git a/Common/Vulkan/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp similarity index 99% rename from Common/Vulkan/VulkanRenderManager.cpp rename to ext/native/thin3d/VulkanRenderManager.cpp index a2e2d7fd1f..195083b35f 100644 --- a/Common/Vulkan/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -1,7 +1,7 @@ #include "base/logging.h" -#include "VulkanRenderManager.h" -#include "VulkanContext.h" +#include "Common/Vulkan/VulkanContext.h" +#include "thin3d/VulkanRenderManager.h" void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; diff --git a/Common/Vulkan/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h similarity index 100% rename from Common/Vulkan/VulkanRenderManager.h rename to ext/native/thin3d/VulkanRenderManager.h diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 967b27c85e..3644db1a12 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -30,11 +30,11 @@ #include "math/lin/matrix4x4.h" #include "math/dataconv.h" #include "thin3d/thin3d.h" +#include "thin3d/VulkanRenderManager.h" #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanImage.h" #include "Common/Vulkan/VulkanMemory.h" -#include "Common/Vulkan/VulkanRenderManager.h" // We use a simple descriptor set for all rendering: 1 sampler, 1 texture, 1 UBO binding point. // binding 0 - uniform data From c788dc896abc1224a2a6872fff60d933eead5619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 17:18:54 +0200 Subject: [PATCH 056/111] Synchronization is HARD. need a rethink, methinks. --- Common/Vulkan/VulkanContext.cpp | 12 +-- ext/native/thin3d/VulkanRenderManager.cpp | 95 +++++++++++++++++------ ext/native/thin3d/VulkanRenderManager.h | 11 ++- ext/native/thin3d/thin3d_vulkan.cpp | 1 - 4 files changed, 85 insertions(+), 34 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 20ec8b0bd3..a49cd58824 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -216,12 +216,6 @@ void VulkanContext::DestroyObjects() { vkDestroySwapchainKHR(device_, swapchain_, nullptr); swapchain_ = VK_NULL_HANDLE; - // If there happen to be any pending deletes, now is a good time. - for (int i = 0; i < ARRAY_SIZE(frame_); i++) { - frame_[i].deleteList.PerformDeletes(device_); - } - Delete().PerformDeletes(device_); - vkDestroySurfaceKHR(instance_, surface_, nullptr); surface_ = VK_NULL_HANDLE; } @@ -768,6 +762,12 @@ VkFence VulkanContext::CreateFence(bool presignalled) { } void VulkanContext::DestroyDevice() { + // If there happen to be any pending deletes, now is a good time. + for (int i = 0; i < ARRAY_SIZE(frame_); i++) { + frame_[i].deleteList.PerformDeletes(device_); + } + Delete().PerformDeletes(device_); + vkDestroyDevice(device_, nullptr); device_ = nullptr; } diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 195083b35f..bedd901a3f 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -2,6 +2,9 @@ #include "Common/Vulkan/VulkanContext.h" #include "thin3d/VulkanRenderManager.h" +#include "thread/threadutil.h" + +const bool useThread = true; void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; @@ -85,19 +88,21 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; cmd_pool_info.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex(); cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPool); + VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolInit); + assert(res == VK_SUCCESS); + res = vkCreateCommandPool(vulkan_->GetDevice(), &cmd_pool_info, nullptr, &frameData_[i].cmdPoolMain); assert(res == VK_SUCCESS); VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - cmd_alloc.commandPool = frameData_[i].cmdPool; + cmd_alloc.commandPool = frameData_[i].cmdPoolInit; cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd_alloc.commandBufferCount = 2; + cmd_alloc.commandBufferCount = 1; - VkCommandBuffer cmdBuf[2]; - res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, cmdBuf); + res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].initCmd); + assert(res == VK_SUCCESS); + cmd_alloc.commandPool = frameData_[i].cmdPoolMain; + res = vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd_alloc, &frameData_[i].mainCmd); assert(res == VK_SUCCESS); - frameData_[i].mainCmd = cmdBuf[0]; - frameData_[i].initCmd = cmdBuf[1]; frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on } } @@ -154,9 +159,21 @@ void VulkanRenderManager::CreateBackbuffers() { InitRenderpasses(); curWidth_ = -1; curHeight_ = -1; + + // Start the thread. + if (useThread) { + run_ = true; + thread_ = std::thread(&VulkanRenderManager::ThreadFunc, this); + } } void VulkanRenderManager::DestroyBackbuffers() { + // Stop the thread. + if (useThread) { + run_ = false; + condVar_.notify_all(); + thread_.join(); + } VkDevice device = vulkan_->GetDevice(); for (uint32_t i = 0; i < swapchainImageCount_; i++) { vulkan_->Delete().QueueDeleteImageView(swapchainImages_[i].view); @@ -168,14 +185,15 @@ void VulkanRenderManager::DestroyBackbuffers() { } VulkanRenderManager::~VulkanRenderManager() { + run_ = false; VkDevice device = vulkan_->GetDevice(); vulkan_->WaitUntilQueueIdle(); vkDestroySemaphore(device, acquireSemaphore_, nullptr); vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; - vkFreeCommandBuffers(device, frameData_[i].cmdPool, 2, cmdBuf); - vkDestroyCommandPool(device, frameData_[i].cmdPool, nullptr); + vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd); + vkFreeCommandBuffers(device, frameData_[i].cmdPoolMain, 1, &frameData_[i].mainCmd); vkDestroyFence(device, frameData_[i].fence, nullptr); } if (backbufferRenderPass_ != VK_NULL_HANDLE) @@ -191,10 +209,15 @@ VulkanRenderManager::~VulkanRenderManager() { // TODO: Activate this code. void VulkanRenderManager::ThreadFunc() { - while (true) { + setCurrentThreadName("RenderMan"); + while (run_) { std::unique_lock lock(mutex_); condVar_.wait(lock); - Flush(); + if (frameAvailable_) { + Run(); + EndFrame(); + frameAvailable_ = false; + } } } @@ -203,12 +226,16 @@ void VulkanRenderManager::BeginFrame() { FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; - // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. - // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage_); - assert(res == VK_SUCCESS); - // Make sure the very last command buffer from the frame before the previous has been fully executed. + if (useThread) { + // Can't wait for this fence until it's actually been enqueued. + // Will replace this with a condvar if it works. + while (!frameData.readyForFence) { + ; + } + frameData.readyForFence = false; + } + vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); @@ -238,7 +265,7 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { void VulkanRenderManager::EndFrame() { insideFrame_ = false; - FrameData &frame = frameData_[vulkan_->GetCurFrame()]; + FrameData &frame = frameData_[curFrame_]; TransitionToPresent(frame.mainCmd, swapchainImages_[curSwapchainImage_].image); @@ -255,13 +282,11 @@ void VulkanRenderManager::EndFrame() { vkEndCommandBuffer(frame.initCmd); cmdBufs.push_back(frame.initCmd); frame.hasInitCommands = false; - ILOG("Frame %d had init commands", vulkan_->GetCurFrame()); + ILOG("Frame %d had init commands", curFrame_); } cmdBufs.push_back(frame.mainCmd); - vulkan_->EndFrame(); - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &acquireSemaphore_; @@ -274,6 +299,10 @@ void VulkanRenderManager::EndFrame() { res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frame.fence); assert(res == VK_SUCCESS); + if (useThread) { + frame.readyForFence = true; + } + VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; present.swapchainCount = 1; @@ -605,22 +634,38 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in } void VulkanRenderManager::Flush() { - { - std::unique_lock lock(mutex_); + curFrame_ = vulkan_->GetCurFrame(); + frameAvailable_ = true; + if (!useThread) { + Run(); + EndFrame(); + } else { + condVar_.notify_all(); + } + vulkan_->EndFrame(); +} + +void VulkanRenderManager::Run() { + //if ({ + // std::unique_lock lock(mutex_); stepsOnThread_ = std::move(steps_); curRenderStep_ = nullptr; - } - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + FrameData &frameData = frameData_[curFrame_]; VkDevice device = vulkan_->GetDevice(); + // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. + // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage_); + assert(res == VK_SUCCESS); + VkCommandBuffer cmd = frameData.mainCmd; VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; begin.pInheritanceInfo = nullptr; - VkResult res = vkBeginCommandBuffer(cmd, &begin); + res = vkBeginCommandBuffer(cmd, &begin); assert(res == VK_SUCCESS); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index e46072a223..ed3237a3fd 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -283,6 +283,7 @@ public: // Can run on a different thread! Just make sure to use BeginFrameWrites. void Flush(); + void Run(); // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); @@ -334,8 +335,11 @@ private: // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { + bool readyForFence = true; VkFence fence; - VkCommandPool cmdPool; + // These are on different threads so need separate pools. + VkCommandPool cmdPoolInit; + VkCommandPool cmdPoolMain; VkCommandBuffer initCmd; VkCommandBuffer mainCmd; bool hasInitCommands = false; @@ -351,8 +355,11 @@ private: std::vector steps_; // Execution time state + int curFrame_; + volatile bool frameAvailable_ = false; + bool run_ = true; VulkanContext *vulkan_; - std::thread submissionThread; + std::thread thread_; std::mutex mutex_; std::condition_variable condVar_; std::vector stepsOnThread_; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 3644db1a12..b8c22cc177 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -766,7 +766,6 @@ void VKContext::EndFrame() { push_->End(); renderManager_.Flush(); - renderManager_.EndFrame(); frameNum_++; if (frameNum_ >= vulkan_->GetInflightFrames()) From 022657ebb36014058c4dcd7b25e5158c91ec9ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 17:44:28 +0200 Subject: [PATCH 057/111] This makes a little more sense, but still hangs (frameAvailable = true while stuck on condvar) --- ext/native/thin3d/VulkanRenderManager.cpp | 53 +++++++++++++---------- ext/native/thin3d/VulkanRenderManager.h | 6 +-- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index bedd901a3f..1cdc6ffe9e 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -214,9 +214,10 @@ void VulkanRenderManager::ThreadFunc() { std::unique_lock lock(mutex_); condVar_.wait(lock); if (frameAvailable_) { - Run(); - EndFrame(); + int frame = threadFrame_; frameAvailable_ = false; + Run(frame); + EndFrame(frame); } } } @@ -224,7 +225,8 @@ void VulkanRenderManager::ThreadFunc() { void VulkanRenderManager::BeginFrame() { VkDevice device = vulkan_->GetDevice(); - FrameData &frameData = frameData_[vulkan_->GetCurFrame()]; + int curFrame = vulkan_->GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; // Make sure the very last command buffer from the frame before the previous has been fully executed. if (useThread) { @@ -236,10 +238,12 @@ void VulkanRenderManager::BeginFrame() { frameData.readyForFence = false; } + ILOG("Fencing %d", curFrame); vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); // Must be after the fence - this performs deletes. + ILOG("BeginFrame %d", curFrame); vulkan_->BeginFrame(); insideFrame_ = true; @@ -258,18 +262,18 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { assert(res == VK_SUCCESS); frameData.hasInitCommands = true; } - return frameData_[vulkan_->GetCurFrame()].initCmd; + return frameData_[curFrame].initCmd; } // After flush. Should probably be part of it? -void VulkanRenderManager::EndFrame() { +void VulkanRenderManager::EndFrame(int frame) { insideFrame_ = false; - FrameData &frame = frameData_[curFrame_]; + FrameData &frameData = frameData_[frame]; - TransitionToPresent(frame.mainCmd, swapchainImages_[curSwapchainImage_].image); + TransitionToPresent(frameData.mainCmd, swapchainImages_[curSwapchainImage_].image); - VkResult res = vkEndCommandBuffer(frame.mainCmd); + VkResult res = vkEndCommandBuffer(frameData.mainCmd); assert(res == VK_SUCCESS); // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. @@ -278,14 +282,13 @@ void VulkanRenderManager::EndFrame() { int numCmdBufs = 0; std::vector cmdBufs; - if (frame.hasInitCommands) { - vkEndCommandBuffer(frame.initCmd); - cmdBufs.push_back(frame.initCmd); - frame.hasInitCommands = false; - ILOG("Frame %d had init commands", curFrame_); + if (frameData.hasInitCommands) { + vkEndCommandBuffer(frameData.initCmd); + cmdBufs.push_back(frameData.initCmd); + frameData.hasInitCommands = false; } - cmdBufs.push_back(frame.mainCmd); + cmdBufs.push_back(frameData.mainCmd); VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit_info.waitSemaphoreCount = 1; @@ -296,11 +299,12 @@ void VulkanRenderManager::EndFrame() { submit_info.pCommandBuffers = cmdBufs.data(); submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &renderingCompleteSemaphore; - res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frame.fence); + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frameData.fence); assert(res == VK_SUCCESS); if (useThread) { - frame.readyForFence = true; + ILOG("Frame %d.readyForFence = true", frame); + frameData.readyForFence = true; } VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); @@ -634,24 +638,28 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in } void VulkanRenderManager::Flush() { - curFrame_ = vulkan_->GetCurFrame(); - frameAvailable_ = true; + while (frameAvailable_) + ; + int curFrame = vulkan_->GetCurFrame(); if (!useThread) { - Run(); - EndFrame(); + Run(curFrame); + EndFrame(curFrame); } else { + frameAvailable_ = true; + threadFrame_ = curFrame; condVar_.notify_all(); } vulkan_->EndFrame(); } -void VulkanRenderManager::Run() { +void VulkanRenderManager::Run(int frame) { + ILOG("Running frame %d", frame); //if ({ // std::unique_lock lock(mutex_); stepsOnThread_ = std::move(steps_); curRenderStep_ = nullptr; - FrameData &frameData = frameData_[curFrame_]; + FrameData &frameData = frameData_[frame]; VkDevice device = vulkan_->GetDevice(); @@ -694,6 +702,7 @@ void VulkanRenderManager::Run() { delete stepsOnThread_[i]; } stepsOnThread_.clear(); + ILOG("Finished running frame %d", frame); } void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index ed3237a3fd..2d8d55c80e 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -205,7 +205,7 @@ public: // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. void BeginFrame(); - void EndFrame(); + void EndFrame(int frame); void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -283,7 +283,7 @@ public: // Can run on a different thread! Just make sure to use BeginFrameWrites. void Flush(); - void Run(); + void Run(int frame); // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); @@ -355,7 +355,7 @@ private: std::vector steps_; // Execution time state - int curFrame_; + int threadFrame_; volatile bool frameAvailable_ = false; bool run_ = true; VulkanContext *vulkan_; From 48ac334f4f16efa928d2857c225934e66235c9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Aug 2017 15:39:23 +0200 Subject: [PATCH 058/111] Vulkan: Don't forget to specify stencil load op in render passes --- ext/native/thin3d/VulkanRenderManager.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 1cdc6ffe9e..214b506f7a 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -4,7 +4,7 @@ #include "thin3d/VulkanRenderManager.h" #include "thread/threadutil.h" -const bool useThread = true; +const bool useThread = false; void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; @@ -559,9 +559,18 @@ void VulkanRenderManager::InitRenderpasses() { for (int depth = 0; depth < 3; depth++) { switch ((VKRRenderPassAction)depth) { - case VKRRenderPassAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case VKRRenderPassAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case VKRRenderPassAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + case VKRRenderPassAction::CLEAR: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + break; + case VKRRenderPassAction::KEEP: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + break; + case VKRRenderPassAction::DONT_CARE: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + break; } for (int color = 0; color < 3; color++) { switch ((VKRRenderPassAction)color) { From d3672d9e57f85356cc7efcf453b01f45d7d8dc0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 22 Aug 2017 18:05:05 +0200 Subject: [PATCH 059/111] Make sure not to begin and end the init command buffer on different threads. --- ext/native/thin3d/VulkanRenderManager.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 214b506f7a..099bf77eda 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -283,7 +283,6 @@ void VulkanRenderManager::EndFrame(int frame) { int numCmdBufs = 0; std::vector cmdBufs; if (frameData.hasInitCommands) { - vkEndCommandBuffer(frameData.initCmd); cmdBufs.push_back(frameData.initCmd); frameData.hasInitCommands = false; } @@ -647,14 +646,20 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in } void VulkanRenderManager::Flush() { - while (frameAvailable_) - ; + curRenderStep_ = nullptr; int curFrame = vulkan_->GetCurFrame(); + FrameData &frameData = frameData_[curFrame]; + if (frameData.hasInitCommands) { + vkEndCommandBuffer(frameData.initCmd); + } if (!useThread) { Run(curFrame); EndFrame(curFrame); } else { - frameAvailable_ = true; + { + std::unique_lock lock(mutex_); + frameAvailable_ = true; + } threadFrame_ = curFrame; condVar_.notify_all(); } @@ -666,7 +671,6 @@ void VulkanRenderManager::Run(int frame) { //if ({ // std::unique_lock lock(mutex_); stepsOnThread_ = std::move(steps_); - curRenderStep_ = nullptr; FrameData &frameData = frameData_[frame]; From 72a41cd5245e68ab053d961eda709d77baab2535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 25 Aug 2017 16:21:45 +0200 Subject: [PATCH 060/111] Let's try a mutex. Doesn't completely solve the problem though.. --- ext/native/thin3d/VulkanRenderManager.cpp | 4 ++++ ext/native/thin3d/VulkanRenderManager.h | 1 + 2 files changed, 5 insertions(+) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 099bf77eda..1d3aed3537 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -215,9 +215,11 @@ void VulkanRenderManager::ThreadFunc() { condVar_.wait(lock); if (frameAvailable_) { int frame = threadFrame_; + frameData_[frame].mutex.lock(); frameAvailable_ = false; Run(frame); EndFrame(frame); + frameData_[frame].mutex.unlock(); } } } @@ -236,6 +238,7 @@ void VulkanRenderManager::BeginFrame() { ; } frameData.readyForFence = false; + frameData.mutex.lock(); } ILOG("Fencing %d", curFrame); @@ -656,6 +659,7 @@ void VulkanRenderManager::Flush() { Run(curFrame); EndFrame(curFrame); } else { + frameData.mutex.unlock(); { std::unique_lock lock(mutex_); frameAvailable_ = true; diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 2d8d55c80e..73c1f3a4d5 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -335,6 +335,7 @@ private: // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { + std::mutex mutex; bool readyForFence = true; VkFence fence; // These are on different threads so need separate pools. From c403edfa890238afd225abf34e767bcebda1907e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Aug 2017 15:55:47 +0200 Subject: [PATCH 061/111] Avoid re-binding pipelines. --- GPU/Software/SoftGpu.cpp | 13 ++++++------- ext/native/thin3d/VulkanRenderManager.cpp | 12 ++++++++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 0a08068a2b..812c164cb2 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -146,13 +146,6 @@ void SoftGPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { if (!draw_) return; - float dstwidth = (float)PSP_CoreParameter().pixelWidth; - float dstheight = (float)PSP_CoreParameter().pixelHeight; - - Draw::Viewport viewport = {0.0f, 0.0f, dstwidth, dstheight, 0.0f, 1.0f}; - draw_->SetViewports(1, &viewport); - draw_->SetScissorRect(0, 0, dstwidth, dstheight); - float u0 = 0.0f; float u1; @@ -216,6 +209,9 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { fbTex = draw_->CreateTexture(desc); + float dstwidth = (float)PSP_CoreParameter().pixelWidth; + float dstheight = (float)PSP_CoreParameter().pixelHeight; + float x, y, w, h; CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, dstwidth, dstheight, ROTATION_LOCKED_HORIZONTAL); @@ -242,6 +238,9 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { std::swap(v0, v1); } draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE }); + Draw::Viewport viewport = { 0.0f, 0.0f, dstwidth, dstheight, 0.0f, 1.0f }; + draw_->SetViewports(1, &viewport); + draw_->SetScissorRect(0, 0, dstwidth, dstheight); Draw::SamplerState *sampler; if (g_Config.iBufFilter == SCALE_NEAREST) { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 1d3aed3537..a4631bc099 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -771,6 +771,8 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer VKRFramebuffer *fb = step.render.framebuffer; + VkPipeline lastPipeline = VK_NULL_HANDLE; + auto &commands = step.commands; // TODO: Dynamic state commands (SetViewport, SetScissor, SetBlendConstants, SetStencil*) are only @@ -798,7 +800,10 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer break; case VKRRenderCommand::DRAW_INDEXED: - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); + if (c.drawIndexed.pipeline != lastPipeline) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); + lastPipeline = c.drawIndexed.pipeline; + } vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); @@ -806,7 +811,10 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer break; case VKRRenderCommand::DRAW: - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); + if (c.draw.pipeline != lastPipeline) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); + lastPipeline = c.draw.pipeline; + } vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); vkCmdDraw(cmd, c.draw.count, 1, 0, 0); From 3e2b810c39f85bff4cd08013f2d58c7a6dceaba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 Aug 2017 17:10:10 +0200 Subject: [PATCH 062/111] Improve synchronization, though there are very weird issues. Possibly need to run the swap chain from the thread that created it? --- Windows/GPU/WindowsVulkanContext.cpp | 2 +- ext/native/thin3d/VulkanRenderManager.cpp | 205 +++++++++++----------- ext/native/thin3d/VulkanRenderManager.h | 35 ++-- 3 files changed, 124 insertions(+), 118 deletions(-) diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 6f87a00cd7..2bddafba38 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -171,7 +171,7 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m Version gitVer(PPSSPP_GIT_VERSION); g_Vulkan = new VulkanContext(); - if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), (g_validate_ ? VULKAN_FLAG_VALIDATE : 0) | VULKAN_FLAG_PRESENT_MAILBOX)) { + if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), (g_validate_ ? VULKAN_FLAG_VALIDATE : 0) | VULKAN_FLAG_PRESENT_FIFO_RELAXED)) { *error_message = g_Vulkan->InitError(); return false; } diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index a4631bc099..5501bba622 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -4,6 +4,7 @@ #include "thin3d/VulkanRenderManager.h" #include "thread/threadutil.h" +// TODO: Using a thread here is unfinished and does not work correctly. const bool useThread = false; void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { @@ -81,7 +82,7 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan semaphoreCreateInfo.flags = 0; VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); assert(res == VK_SUCCESS); - res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore); + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore_); assert(res == VK_SUCCESS); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { @@ -111,8 +112,6 @@ void VulkanRenderManager::CreateBackbuffers() { VkResult res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, nullptr); assert(res == VK_SUCCESS); - ILOG("Vulkan swapchain image count: %d", swapchainImageCount_); - VkImage* swapchainImages = new VkImage[swapchainImageCount_]; res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, swapchainImages); assert(res == VK_SUCCESS); @@ -151,7 +150,6 @@ void VulkanRenderManager::CreateBackbuffers() { assert(res == VK_SUCCESS); } delete[] swapchainImages; - curSwapchainImage_ = -1; InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. InitBackbufferRenderPass(); // Must be before InitFramebuffers. @@ -168,10 +166,19 @@ void VulkanRenderManager::CreateBackbuffers() { } void VulkanRenderManager::DestroyBackbuffers() { - // Stop the thread. if (useThread) { run_ = false; - condVar_.notify_all(); + // Stop the thread. + for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { + { + std::unique_lock lock(frameData_[i].push_mutex); + frameData_[i].push_condVar.notify_all(); + } + { + std::unique_lock lock(frameData_[i].pull_mutex); + frameData_[i].pull_condVar.notify_all(); + } + } thread_.join(); } VkDevice device = vulkan_->GetDevice(); @@ -189,7 +196,7 @@ VulkanRenderManager::~VulkanRenderManager() { VkDevice device = vulkan_->GetDevice(); vulkan_->WaitUntilQueueIdle(); vkDestroySemaphore(device, acquireSemaphore_, nullptr); - vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr); + vkDestroySemaphore(device, renderingCompleteSemaphore_, nullptr); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd); @@ -210,17 +217,25 @@ VulkanRenderManager::~VulkanRenderManager() { // TODO: Activate this code. void VulkanRenderManager::ThreadFunc() { setCurrentThreadName("RenderMan"); + int threadFrame = -1; // Increment first, start at 0. while (run_) { - std::unique_lock lock(mutex_); - condVar_.wait(lock); - if (frameAvailable_) { - int frame = threadFrame_; - frameData_[frame].mutex.lock(); - frameAvailable_ = false; - Run(frame); - EndFrame(frame); - frameData_[frame].mutex.unlock(); + { + threadFrame++; + if (threadFrame >= vulkan_->GetInflightFrames()) + threadFrame = 0; + FrameData &frameData = frameData_[threadFrame]; + std::unique_lock lock(frameData.pull_mutex); + while (!frameData.readyForRun && run_) { + ILOG("PULL: Waiting for frame[%d].readyForRun", threadFrame); + frameData.pull_condVar.wait(lock); + } + ILOG("PULL: frame[%d].readyForRun = false", threadFrame); + frameData.readyForRun = false; + if (!run_) // quick exit if bailing. + return; } + ILOG("PULL: Running frame %d", threadFrame); + Run(threadFrame); } } @@ -232,21 +247,20 @@ void VulkanRenderManager::BeginFrame() { // Make sure the very last command buffer from the frame before the previous has been fully executed. if (useThread) { - // Can't wait for this fence until it's actually been enqueued. - // Will replace this with a condvar if it works. + std::unique_lock lock(frameData.push_mutex); while (!frameData.readyForFence) { - ; + ILOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); + frameData.push_condVar.wait(lock); } frameData.readyForFence = false; - frameData.mutex.lock(); } - - ILOG("Fencing %d", curFrame); + + ILOG("PUSH: Fencing %d", curFrame); vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); // Must be after the fence - this performs deletes. - ILOG("BeginFrame %d", curFrame); + ILOG("PUSH: BeginFrame %d", curFrame); vulkan_->BeginFrame(); insideFrame_ = true; @@ -268,61 +282,6 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { return frameData_[curFrame].initCmd; } -// After flush. Should probably be part of it? -void VulkanRenderManager::EndFrame(int frame) { - insideFrame_ = false; - - FrameData &frameData = frameData_[frame]; - - TransitionToPresent(frameData.mainCmd, swapchainImages_[curSwapchainImage_].image); - - VkResult res = vkEndCommandBuffer(frameData.mainCmd); - assert(res == VK_SUCCESS); - - // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. - // This way we bunch up all the initialization needed for the frame, we render to - // other buffers before the back buffer, and then last we render to the backbuffer. - - int numCmdBufs = 0; - std::vector cmdBufs; - if (frameData.hasInitCommands) { - cmdBufs.push_back(frameData.initCmd); - frameData.hasInitCommands = false; - } - - cmdBufs.push_back(frameData.mainCmd); - - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &acquireSemaphore_; - VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; - submit_info.pWaitDstStageMask = waitStage; - submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); - submit_info.pCommandBuffers = cmdBufs.data(); - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &renderingCompleteSemaphore; - res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frameData.fence); - assert(res == VK_SUCCESS); - - if (useThread) { - ILOG("Frame %d.readyForFence = true", frame); - frameData.readyForFence = true; - } - - VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); - VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; - present.swapchainCount = 1; - present.pSwapchains = &swapchain; - present.pImageIndices = &curSwapchainImage_; - present.pWaitSemaphores = &renderingCompleteSemaphore; - present.waitSemaphoreCount = 1; - present.pResults = nullptr; - res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); - // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI - // return codes - assert(res == VK_SUCCESS); -} - void VulkanRenderManager::Sync() { } @@ -656,33 +615,27 @@ void VulkanRenderManager::Flush() { vkEndCommandBuffer(frameData.initCmd); } if (!useThread) { + frameData.steps = std::move(steps_); Run(curFrame); - EndFrame(curFrame); } else { - frameData.mutex.unlock(); - { - std::unique_lock lock(mutex_); - frameAvailable_ = true; - } - threadFrame_ = curFrame; - condVar_.notify_all(); + std::unique_lock lock(frameData.pull_mutex); + ILOG("PUSH: Frame[%d].readyForRun = true", curFrame); + frameData.steps = std::move(steps_); + frameData.readyForRun = true; + frameData.pull_condVar.notify_all(); } vulkan_->EndFrame(); } void VulkanRenderManager::Run(int frame) { - ILOG("Running frame %d", frame); - //if ({ - // std::unique_lock lock(mutex_); - stepsOnThread_ = std::move(steps_); - FrameData &frameData = frameData_[frame]; - + auto &stepsOnThread_ = frameData_[frame].steps; VkDevice device = vulkan_->GetDevice(); + uint32_t curSwapchainImage = 0; // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage_); + VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage); assert(res == VK_SUCCESS); VkCommandBuffer cmd = frameData.mainCmd; @@ -697,14 +650,14 @@ void VulkanRenderManager::Run(int frame) { // return codes // TODO: Is it best to do this here, or combine with some other transition, or just do it right before the backbuffer bind-for-render? assert(res == VK_SUCCESS); - TransitionFromPresent(cmd, swapchainImages_[curSwapchainImage_].image); + TransitionFromPresent(cmd, swapchainImages_[curSwapchainImage].image); // Optimizes renderpasses, then sequences them. for (int i = 0; i < stepsOnThread_.size(); i++) { const VKRStep &step = *stepsOnThread_[i]; switch (step.stepType) { case VKRStepType::RENDER: - PerformRenderPass(step, cmd); + PerformRenderPass(step, cmd, curSwapchainImage); break; case VKRStepType::COPY: PerformCopy(step, cmd); @@ -719,10 +672,62 @@ void VulkanRenderManager::Run(int frame) { delete stepsOnThread_[i]; } stepsOnThread_.clear(); - ILOG("Finished running frame %d", frame); + insideFrame_ = false; + + TransitionToPresent(frameData.mainCmd, swapchainImages_[curSwapchainImage].image); + + res = vkEndCommandBuffer(frameData.mainCmd); + assert(res == VK_SUCCESS); + + // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. + // This way we bunch up all the initialization needed for the frame, we render to + // other buffers before the back buffer, and then last we render to the backbuffer. + + int numCmdBufs = 0; + std::vector cmdBufs; + if (frameData.hasInitCommands) { + cmdBufs.push_back(frameData.initCmd); + frameData.hasInitCommands = false; + } + + cmdBufs.push_back(frameData.mainCmd); + + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &acquireSemaphore_; + VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; + submit_info.pWaitDstStageMask = waitStage; + submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); + submit_info.pCommandBuffers = cmdBufs.data(); + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frameData.fence); + assert(res == VK_SUCCESS); + + if (useThread) { + ILOG("PULL: Frame %d.readyForFence = true", frame); + std::unique_lock lock(frameData.push_mutex); + frameData.readyForFence = true; + frameData.push_condVar.notify_all(); + } + + VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); + VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; + present.swapchainCount = 1; + present.pSwapchains = &swapchain; + present.pImageIndices = &curSwapchainImage; + present.pWaitSemaphores = &renderingCompleteSemaphore_; + present.waitSemaphoreCount = 1; + present.pResults = nullptr; + res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); + // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI + // return codes + assert(res == VK_SUCCESS); + + ILOG("PULL: Finished running frame %d", frame); } -void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { +void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd, int swapChainImage) { // TODO: If there are multiple, we can transition them together. for (const auto &iter : step.preTransitions) { if (iter.fb->color.layout != iter.targetLayout) { @@ -767,7 +772,7 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer } } - PerformBindFramebufferAsRenderTarget(step, cmd); + PerformBindFramebufferAsRenderTarget(step, cmd, swapChainImage); VKRFramebuffer *fb = step.render.framebuffer; @@ -895,7 +900,7 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer } } -void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { +void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd, int swapChainImage) { VkFramebuffer framebuf; int w; int h; @@ -907,7 +912,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st h = fb->height; prevLayout = fb->color.layout; } else { - framebuf = framebuffers_[curSwapchainImage_]; + framebuf = framebuffers_[swapChainImage]; w = vulkan_->GetBackbufferWidth(); h = vulkan_->GetBackbufferHeight(); } diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 73c1f3a4d5..e6ee367451 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -205,7 +205,12 @@ public: // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. void BeginFrame(); - void EndFrame(int frame); + // Can run on a different thread! Just make sure to use BeginFrameWrites. + void Flush(); + void Run(int frame); + // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). + void Sync(); + void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -281,13 +286,6 @@ public: curRenderStep_->render.numDraws++; } - // Can run on a different thread! Just make sure to use BeginFrameWrites. - void Flush(); - void Run(int frame); - - // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). - void Sync(); - VkCommandBuffer GetInitCmd(); VkRenderPass GetBackbufferRenderpass() const { return backbufferRenderPass_; @@ -312,9 +310,9 @@ private: void InitRenderpasses(); void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. - void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd); + void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd, int swapChainImage); - void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd); + void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd, int swapChainImage); void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd); void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); @@ -327,7 +325,7 @@ private: // Permanent objects VkSemaphore acquireSemaphore_; - VkSemaphore renderingCompleteSemaphore; + VkSemaphore renderingCompleteSemaphore_; VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. // TODO: Create these on demand. @@ -335,8 +333,15 @@ private: // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { - std::mutex mutex; + std::mutex push_mutex; + std::condition_variable push_condVar; + + std::mutex pull_mutex; + std::condition_variable pull_condVar; + bool readyForFence = true; + + bool readyForRun = false; VkFence fence; // These are on different threads so need separate pools. VkCommandPool cmdPoolInit; @@ -344,6 +349,7 @@ private: VkCommandBuffer initCmd; VkCommandBuffer mainCmd; bool hasInitCommands = false; + std::vector steps; }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; @@ -356,14 +362,10 @@ private: std::vector steps_; // Execution time state - int threadFrame_; - volatile bool frameAvailable_ = false; bool run_ = true; VulkanContext *vulkan_; std::thread thread_; std::mutex mutex_; - std::condition_variable condVar_; - std::vector stepsOnThread_; VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; // Swap chain management @@ -374,7 +376,6 @@ private: std::vector framebuffers_; std::vector swapchainImages_; uint32_t swapchainImageCount_; - uint32_t curSwapchainImage_ = 0; struct DepthBufferInfo { VkFormat format = VK_FORMAT_UNDEFINED; VkImage image = VK_NULL_HANDLE; From b5e06f3c7dda7f18f09e631d14856523922c5410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 23 Oct 2017 14:53:35 +0200 Subject: [PATCH 063/111] VulkanRenderManager: BindPipeline should really be its own command. Tighten up some image transitions. --- GPU/Vulkan/DrawEngineVulkan.cpp | 16 +++--- GPU/Vulkan/FramebufferVulkan.cpp | 3 +- ext/native/thin3d/VulkanRenderManager.cpp | 67 ++++++++++++++--------- ext/native/thin3d/VulkanRenderManager.h | 22 +++++--- ext/native/thin3d/thin3d_vulkan.cpp | 12 +++- 5 files changed, 75 insertions(+), 45 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 0cc249cf9a..3d326d7556 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -866,7 +866,6 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } - VulkanPipeline *pipeline = lastPipeline_; if (!lastPipeline_ || !gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { @@ -874,11 +873,12 @@ void DrawEngineVulkan::DoFlush() { } Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); - pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); if (!pipeline) { // Already logged, let's bail out. return; } + renderManager->BindPipeline(pipeline->pipeline); if (pipeline != lastPipeline_) { if (lastPipeline_ && !lastPipeline_->useBlendConstant && pipeline->useBlendConstant) { gstate_c.Dirty(DIRTY_BLEND_STATE); @@ -910,9 +910,9 @@ void DrawEngineVulkan::DoFlush() { if (!ibuf) ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); int numInstances = (gstate_c.bezier || gstate_c.spline) ? numPatches : 1; - renderManager->DrawIndexed(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, numInstances, VK_INDEX_TYPE_UINT16); + renderManager->DrawIndexed(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, numInstances, VK_INDEX_TYPE_UINT16); } else { - renderManager->Draw(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, vertexCount); + renderManager->Draw(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, vertexCount); } } } else { @@ -966,7 +966,6 @@ void DrawEngineVulkan::DoFlush() { if (sampler == VK_NULL_HANDLE) sampler = nullSampler_; } - VulkanPipeline *pipeline = lastPipeline_; if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { @@ -974,7 +973,8 @@ void DrawEngineVulkan::DoFlush() { } Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); - pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); + renderManager->BindPipeline(pipeline->pipeline); if (!pipeline) { // Already logged, let's bail out. return; @@ -1008,12 +1008,12 @@ void DrawEngineVulkan::DoFlush() { vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf); ibOffset = (uint32_t)frame->pushIndex->Push(inds, sizeof(short) * numTrans, &ibuf); VkDeviceSize offsets[1] = { vbOffset }; - renderManager->DrawIndexed(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, numTrans, 1, VK_INDEX_TYPE_UINT16); + renderManager->DrawIndexed(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, numTrans, 1, VK_INDEX_TYPE_UINT16); } else { VkBuffer vbuf; vbOffset = (uint32_t)frame->pushVertex->Push(drawBuffer, numTrans * sizeof(TransformedVertex), &vbuf); VkDeviceSize offsets[1] = { vbOffset }; - renderManager->Draw(pipeline->pipeline, pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, numTrans); + renderManager->Draw(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, numTrans); } } else if (result.action == SW_CLEAR) { // Note: we won't get here if the clear is alpha but not color, or color but not alpha. diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index bf33417a4a..a9b5d0a997 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -367,7 +367,8 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa VkDescriptorSet descSet = vulkan2D_.GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); VkBuffer vbuffer; VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer); - renderManager->Draw(cur2DPipeline_, vulkan2D_.GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); + renderManager->BindPipeline(cur2DPipeline_); + renderManager->Draw(vulkan2D_.GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); } void FramebufferManagerVulkan::Bind2DShader() { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 5501bba622..f7357b86e8 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -749,6 +749,10 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; default: Crash(); break; @@ -786,6 +790,13 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer for (const auto &c : commands) { switch (c.cmd) { + case VKRRenderCommand::BIND_PIPELINE: + if (c.pipeline.pipeline != lastPipeline) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.pipeline.pipeline); + lastPipeline = c.pipeline.pipeline; + } + break; + case VKRRenderCommand::VIEWPORT: vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp); break; @@ -805,10 +816,6 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer break; case VKRRenderCommand::DRAW_INDEXED: - if (c.drawIndexed.pipeline != lastPipeline) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipeline); - lastPipeline = c.drawIndexed.pipeline; - } vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); @@ -816,10 +823,6 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer break; case VKRRenderCommand::DRAW: - if (c.draw.pipeline != lastPipeline) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipeline); - lastPipeline = c.draw.pipeline; - } vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); vkCmdDraw(cmd, c.draw.count, 1, 0, 0); @@ -1061,32 +1064,33 @@ void VulkanRenderManager::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) int srcCount = 0; int dstCount = 0; + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; // First source barriers. if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); } if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); } } // We can't copy only depth or only stencil unfortunately. if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); } if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); } } - // TODO: Fix the pipe bits to be bit less conservative. if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); } if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); } if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { @@ -1137,32 +1141,33 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) blit.dstSubresource.mipLevel = 0; blit.dstSubresource.layerCount = 1; + VkPipelineStageFlags srcStage = 0; + VkPipelineStageFlags dstStage = 0; // First source barriers. if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT); + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); } if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT); + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); } } // We can't copy only depth or only stencil unfortunately. if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); } if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); } } - // TODO: Fix the pipe bits to be bit less conservative. if (srcCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); } if (dstCount) { - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); } if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { @@ -1185,7 +1190,7 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) } } -void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { +void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = img.layout; barrier.subresourceRange.layerCount = 1; @@ -1194,16 +1199,20 @@ void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMem barrier.srcAccessMask = 0; switch (img.layout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; default: Crash(); @@ -1211,10 +1220,12 @@ void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMem barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; img.layout = barrier.newLayout; } -void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) { +void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.oldLayout = img.layout; barrier.subresourceRange.layerCount = 1; @@ -1224,15 +1235,19 @@ void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMem switch (img.layout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; default: Crash(); @@ -1240,5 +1255,7 @@ void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMem barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; img.layout = barrier.newLayout; } diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index e6ee367451..6f4ffb5bff 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -16,6 +16,7 @@ // The cool thing is that you can Flush on a different thread than you record the commands on! enum class VKRRenderCommand : uint8_t { + BIND_PIPELINE, STENCIL, BLEND, VIEWPORT, @@ -30,6 +31,8 @@ struct VkRenderData { union { struct { VkPipeline pipeline; + } pipeline; + struct { VkPipelineLayout pipelineLayout; VkDescriptorSet ds; int numUboOffsets; @@ -39,7 +42,6 @@ struct VkRenderData { int count; } draw; struct { - VkPipeline pipeline; VkPipelineLayout pipelineLayout; VkDescriptorSet ds; int numUboOffsets; @@ -58,7 +60,6 @@ struct VkRenderData { int clearStencil; int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc } clear; - struct { VkViewport vp; } viewport; @@ -218,6 +219,13 @@ public: void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask); void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter); + void BindPipeline(VkPipeline pipeline) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + VkRenderData data{ VKRRenderCommand::BIND_PIPELINE }; + data.pipeline.pipeline = pipeline; + curRenderStep_->commands.push_back(data); + } + void SetViewport(const VkViewport &vp) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::VIEWPORT }; @@ -250,11 +258,10 @@ public: void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); - void Draw(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { + void Draw(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::DRAW }; data.draw.count = count; - data.draw.pipeline = pipeline; data.draw.pipelineLayout = layout; data.draw.ds = descSet; data.draw.vbuffer = vbuffer; @@ -266,12 +273,11 @@ public: curRenderStep_->render.numDraws++; } - void DrawIndexed(VkPipeline pipeline, VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { + void DrawIndexed(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); VkRenderData data{ VKRRenderCommand::DRAW_INDEXED }; data.drawIndexed.count = count; data.drawIndexed.instances = numInstances; - data.drawIndexed.pipeline = pipeline; data.drawIndexed.pipelineLayout = layout; data.drawIndexed.ds = descSet; data.drawIndexed.vbuffer = vbuffer; @@ -320,8 +326,8 @@ private: return (int)depth * 3 + (int)color; } - static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); - static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect); + static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); + static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); // Permanent objects VkSemaphore acquireSemaphore_; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b8c22cc177..b3a323ca64 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1114,7 +1114,9 @@ void VKContext::Draw(int vertexCount, int offset) { VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); + renderManager_.BindPipeline(curPipeline_->vkpipeline); + // TODO: blend constants, stencil, viewports should be here, after bindpipeline.. + renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } void VKContext::DrawIndexed(int vertexCount, int offset) { @@ -1128,7 +1130,9 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.DrawIndexed(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, 1, VK_INDEX_TYPE_UINT32); + renderManager_.BindPipeline(curPipeline_->vkpipeline); + // TODO: blend constants, stencil, viewports should be here, after bindpipeline.. + renderManager_.DrawIndexed(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, vertexCount, 1, VK_INDEX_TYPE_UINT32); } void VKContext::DrawUP(const void *vdata, int vertexCount) { @@ -1140,7 +1144,9 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { VkDeviceSize offsets[1] = { vbBindOffset }; VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); - renderManager_.Draw(curPipeline_->vkpipeline, pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); + renderManager_.BindPipeline(curPipeline_->vkpipeline); + // TODO: blend constants, stencil, viewports should be here, after bindpipeline.. + renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } // TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. From c4f0afc8a28da597f713be466a5b574b6cece298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 9 Oct 2017 12:43:56 +0200 Subject: [PATCH 064/111] Clean out some obsolete code --- GPU/Vulkan/FramebufferVulkan.cpp | 42 +++-------------------- GPU/Vulkan/VulkanUtil.cpp | 27 --------------- GPU/Vulkan/VulkanUtil.h | 27 --------------- ext/native/thin3d/VulkanQueueRunner.cpp | 1 + ext/native/thin3d/VulkanRenderManager.cpp | 10 +++--- 5 files changed, 9 insertions(+), 98 deletions(-) diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index a9b5d0a997..f9c6946e97 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -175,7 +175,6 @@ void FramebufferManagerVulkan::DestroyDeviceObjects() { } void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth) { - // if (!useBufferedRendering_) { float x, y, w, h; CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); @@ -196,9 +195,6 @@ void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, boo if (clearDepth) { SetDepthUpdated(); } - //} else { - // TODO: Clever render pass magic. - //} } void FramebufferManagerVulkan::UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight) { @@ -486,50 +482,20 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V } bool FramebufferManagerVulkan::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - // When updating VRAM, it need to be exact format. - if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) { - switch (nvfb->format) { - case GE_FORMAT_4444: - nvfb->colorDepth = VK_FBO_4444; - break; - case GE_FORMAT_5551: - nvfb->colorDepth = VK_FBO_5551; - break; - case GE_FORMAT_565: - nvfb->colorDepth = VK_FBO_565; - break; - case GE_FORMAT_8888: - default: - nvfb->colorDepth = VK_FBO_8888; - break; - } - } + nvfb->colorDepth = Draw::FBO_8888; - /* - nvfb->fbo = CreateFramebuffer(nvfb->width, nvfb->height, 1, false, (FBOColorDepth)nvfb->colorDepth); + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->width, nvfb->height, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); if (!(nvfb->fbo)) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); return false; } - BindFramebufferAsRenderTarget(nvfb->fbo); - */ + draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); return true; } void FramebufferManagerVulkan::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) { - // _assert_msg_(G3D, nvfb->fbo, "Expecting a valid nvfb in UpdateDownloadTempBuffer"); - - // Discard the previous contents of this buffer where possible. - /* - if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) { - BindFramebufferAsRenderTargetnvfb->fbo); - GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT }; - glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, attachments); - } else if (gl_extensions.IsGLES) { - BindFramebufferAsRenderTargetnvfb->fbo); - } - */ + // Nothing to do here. } void FramebufferManagerVulkan::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) { diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index 576315ff9b..caf06cbac8 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -20,33 +20,6 @@ #include "Common/Vulkan/VulkanContext.h" #include "GPU/Vulkan/VulkanUtil.h" -VulkanFBO::VulkanFBO() : color_(nullptr), depthStencil_(nullptr) {} - -VulkanFBO::~VulkanFBO() { - delete color_; - delete depthStencil_; -} - -void VulkanFBO::Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat color_Format) { - color_ = new VulkanTexture(vulkan); - VkImageCreateFlags flags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - color_->CreateDirect(cmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, nullptr); - depthStencil_->CreateDirect(cmd, width, height, 1, VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, flags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, nullptr); - - VkImageView views[2] = { color_->GetImageView(), depthStencil_->GetImageView() }; - - VkFramebufferCreateInfo fb = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - fb.pAttachments = views; - fb.attachmentCount = 2; - fb.flags = 0; - fb.renderPass = rp_compatible; - fb.width = width; - fb.height = height; - fb.layers = 1; - - vkCreateFramebuffer(vulkan->GetDevice(), &fb, nullptr, &framebuffer_); -} - Vulkan2D::Vulkan2D(VulkanContext *vulkan) : vulkan_(vulkan), curFrame_(0) { InitDeviceObjects(); } diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index fefb35544b..3c32a63fac 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -42,33 +42,6 @@ // // Each FBO will get its own command buffer for each pass. -// -struct VulkanFBOPass { - VkCommandBuffer cmd; -}; - -class VulkanFBO { -public: - VulkanFBO(); - ~VulkanFBO(); - - // Depth-format is chosen automatically depending on hardware support. - // Color format will be 32-bit RGBA. - void Create(VulkanContext *vulkan, VkCommandBuffer cmd, VkRenderPass rp_compatible, int width, int height, VkFormat colorFormat); - - VulkanTexture *GetColor() { return color_; } - VulkanTexture *GetDepthStencil() { return depthStencil_; } - - VkFramebuffer GetFramebuffer() { return framebuffer_; } - -private: - VulkanTexture *color_; - VulkanTexture *depthStencil_; - - // This point specifically to color and depth. - VkFramebuffer framebuffer_; -}; - // Similar to a subset of Thin3D, but separate. // This is used for things like postprocessing shaders, depal, etc. // No UBO data is used, only PushConstants. diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index e69de29bb2..ad86d0bcec 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -0,0 +1 @@ +#include "VulkanQueueRunner.h" diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index f7357b86e8..804b677977 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -80,9 +80,9 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) { VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; semaphoreCreateInfo.flags = 0; - VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &acquireSemaphore_); + VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore_); assert(res == VK_SUCCESS); - res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, NULL, &renderingCompleteSemaphore_); + res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore_); assert(res == VK_SUCCESS); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { @@ -145,7 +145,7 @@ void VulkanRenderManager::CreateBackbuffers() { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, NULL, &sc_buffer.view); + res = vkCreateImageView(vulkan_->GetDevice(), &color_image_view, nullptr, &sc_buffer.view); swapchainImages_.push_back(sc_buffer); assert(res == VK_SUCCESS); } @@ -267,8 +267,6 @@ void VulkanRenderManager::BeginFrame() { } VkCommandBuffer VulkanRenderManager::GetInitCmd() { - // assert(insideFrame_ || firstFrame_); - int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (!frameData.hasInitCommands) { @@ -389,7 +387,7 @@ void VulkanRenderManager::InitBackbufferRenderPass() { rp_info.dependencyCount = 0; rp_info.pDependencies = nullptr; - res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, NULL, &backbufferRenderPass_); + res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, nullptr, &backbufferRenderPass_); assert(res == VK_SUCCESS); } From d016bfe2219bd274ef92d89a88a2cead886ffed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 17:19:00 +0200 Subject: [PATCH 065/111] Fix some resource leaks and excessive logging in VulkanRenderManager --- Windows/GPU/WindowsVulkanContext.cpp | 9 +++- ext/native/thin3d/VulkanRenderManager.cpp | 56 ++++++++++++++--------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 2bddafba38..2c4a8992f4 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -171,7 +171,13 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m Version gitVer(PPSSPP_GIT_VERSION); g_Vulkan = new VulkanContext(); - if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), (g_validate_ ? VULKAN_FLAG_VALIDATE : 0) | VULKAN_FLAG_PRESENT_FIFO_RELAXED)) { + + // int vulkanFlags = VULKAN_FLAG_PRESENT_FIFO_RELAXED; + int vulkanFlags = VULKAN_FLAG_PRESENT_MAILBOX; + if (g_validate_) { + vulkanFlags |= VULKAN_FLAG_VALIDATE; + } + if (VK_SUCCESS != g_Vulkan->CreateInstance("PPSSPP", gitVer.ToInteger(), vulkanFlags)) { *error_message = g_Vulkan->InitError(); return false; } @@ -225,6 +231,7 @@ void WindowsVulkanContext::Resize() { g_Vulkan->DestroyObjects(); g_Vulkan->ReinitSurfaceWin32(); + g_Vulkan->InitObjects(); draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 804b677977..6a058eeed7 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -4,6 +4,12 @@ #include "thin3d/VulkanRenderManager.h" #include "thread/threadutil.h" +#ifdef _DEBUG +#define VLOG ILOG +#else +#define VLOG(...) +#endif + // TODO: Using a thread here is unfinished and does not work correctly. const bool useThread = false; @@ -106,6 +112,9 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan assert(res == VK_SUCCESS); frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on } + + InitBackbufferRenderPass(); + InitRenderpasses(); } void VulkanRenderManager::CreateBackbuffers() { @@ -152,9 +161,7 @@ void VulkanRenderManager::CreateBackbuffers() { delete[] swapchainImages; InitDepthStencilBuffer(cmdInit); // Must be before InitBackbufferRenderPass. - InitBackbufferRenderPass(); // Must be before InitFramebuffers. InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); - InitRenderpasses(); curWidth_ = -1; curHeight_ = -1; @@ -188,6 +195,12 @@ void VulkanRenderManager::DestroyBackbuffers() { vulkan_->Delete().QueueDeleteImageView(depth_.view); vulkan_->Delete().QueueDeleteImage(depth_.image); vulkan_->Delete().QueueDeleteDeviceMemory(depth_.mem); + for (uint32_t i = 0; i < framebuffers_.size(); i++) { + assert(framebuffers_[i] != VK_NULL_HANDLE); + vulkan_->Delete().QueueDeleteFramebuffer(framebuffers_[i]); + } + framebuffers_.clear(); + swapchainImages_.clear(); } @@ -201,17 +214,16 @@ VulkanRenderManager::~VulkanRenderManager() { VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd); vkFreeCommandBuffers(device, frameData_[i].cmdPoolMain, 1, &frameData_[i].mainCmd); + vkDestroyCommandPool(device, frameData_[i].cmdPoolInit, nullptr); + vkDestroyCommandPool(device, frameData_[i].cmdPoolMain, nullptr); vkDestroyFence(device, frameData_[i].fence, nullptr); } - if (backbufferRenderPass_ != VK_NULL_HANDLE) - vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); - for (uint32_t i = 0; i < framebuffers_.size(); i++) { - vkDestroyFramebuffer(device, framebuffers_[i], nullptr); - } - framebuffers_.clear(); for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { + assert(renderPasses_[i] != VK_NULL_HANDLE); vkDestroyRenderPass(device, renderPasses_[i], nullptr); } + assert(backbufferRenderPass_ != VK_NULL_HANDLE); + vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); } // TODO: Activate this code. @@ -226,15 +238,15 @@ void VulkanRenderManager::ThreadFunc() { FrameData &frameData = frameData_[threadFrame]; std::unique_lock lock(frameData.pull_mutex); while (!frameData.readyForRun && run_) { - ILOG("PULL: Waiting for frame[%d].readyForRun", threadFrame); + VLOG("PULL: Waiting for frame[%d].readyForRun", threadFrame); frameData.pull_condVar.wait(lock); } - ILOG("PULL: frame[%d].readyForRun = false", threadFrame); + VLOG("PULL: frame[%d].readyForRun = false", threadFrame); frameData.readyForRun = false; if (!run_) // quick exit if bailing. return; } - ILOG("PULL: Running frame %d", threadFrame); + VLOG("PULL: Running frame %d", threadFrame); Run(threadFrame); } } @@ -249,18 +261,18 @@ void VulkanRenderManager::BeginFrame() { if (useThread) { std::unique_lock lock(frameData.push_mutex); while (!frameData.readyForFence) { - ILOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); + VLOG("PUSH: Waiting for frame[%d].readyForFence = 1", curFrame); frameData.push_condVar.wait(lock); } frameData.readyForFence = false; } - ILOG("PUSH: Fencing %d", curFrame); + VLOG("PUSH: Fencing %d", curFrame); vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX); vkResetFences(device, 1, &frameData.fence); // Must be after the fence - this performs deletes. - ILOG("PUSH: BeginFrame %d", curFrame); + VLOG("PUSH: BeginFrame %d", curFrame); vulkan_->BeginFrame(); insideFrame_ = true; @@ -315,7 +327,7 @@ void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { // We share the same depth buffer but have multiple color buffers, see the loop below. VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view }; - ILOG("InitFramebuffers: %dx%d", width, height); + VLOG("InitFramebuffers: %dx%d", width, height); VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; fb_info.renderPass = backbufferRenderPass_; fb_info.attachmentCount = 2; @@ -347,8 +359,7 @@ void VulkanRenderManager::InitBackbufferRenderPass() { attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachments[0].flags = 0; - assert(depth_.format != VK_FORMAT_UNDEFINED); - attachments[1].format = depth_.format; + attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; // must use this same format later for the back depth buffer. attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -537,7 +548,8 @@ void VulkanRenderManager::InitRenderpasses() { case VKRRenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; case VKRRenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; } - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((VKRRenderPassAction)color, (VKRRenderPassAction)depth)]); + int index = RPIndex((VKRRenderPassAction)color, (VKRRenderPassAction)depth); + vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[index]); } } } @@ -617,7 +629,7 @@ void VulkanRenderManager::Flush() { Run(curFrame); } else { std::unique_lock lock(frameData.pull_mutex); - ILOG("PUSH: Frame[%d].readyForRun = true", curFrame); + VLOG("PUSH: Frame[%d].readyForRun = true", curFrame); frameData.steps = std::move(steps_); frameData.readyForRun = true; frameData.pull_condVar.notify_all(); @@ -703,7 +715,7 @@ void VulkanRenderManager::Run(int frame) { assert(res == VK_SUCCESS); if (useThread) { - ILOG("PULL: Frame %d.readyForFence = true", frame); + VLOG("PULL: Frame %d.readyForFence = true", frame); std::unique_lock lock(frameData.push_mutex); frameData.readyForFence = true; frameData.push_condVar.notify_all(); @@ -722,7 +734,7 @@ void VulkanRenderManager::Run(int frame) { // return codes assert(res == VK_SUCCESS); - ILOG("PULL: Finished running frame %d", frame); + VLOG("PULL: Finished running frame %d", frame); } void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd, int swapChainImage) { @@ -1006,7 +1018,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st } renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; - // ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); + // VLOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); if (step.render.color == VKRRenderPassAction::CLEAR) { Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); numClearVals = 1; From 8639664a6a09bb1c594bc1f322b7e52ac3b040fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 19:05:09 +0200 Subject: [PATCH 066/111] Warning fixes, CMakeLists.txt/Android.mk fixes --- ext/native/Android.mk | 2 ++ ext/native/thin3d/thin3d_vulkan.cpp | 20 ++++---------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 14310e2276..ad1271daba 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -85,6 +85,8 @@ LOCAL_SRC_FILES :=\ thin3d/thin3d.cpp \ thin3d/thin3d_gl.cpp \ thin3d/thin3d_vulkan.cpp \ + thin3d/VulkanRenderManager.cpp \ + thin3d/VulkanQueueRunner.cpp \ ui/view.cpp \ ui/viewgroup.cpp \ ui/ui.cpp \ diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b3a323ca64..5a371da48f 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -135,14 +135,6 @@ static const VkStencilOp stencilOpToVK[8] = { VK_STENCIL_OP_DECREMENT_AND_WRAP, }; -// TODO: Replace with the one from dataconv -static inline void Uint8x4ToFloat4(uint32_t u, float f[4]) { - f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f); - f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f); - f[2] = ((u >> 16) & 0xFF) * (1.0f / 255.0f); - f[3] = ((u >> 24) & 0xFF) * (1.0f / 255.0f); -} - class VKBlendState : public BlendState { public: VkPipelineColorBlendStateCreateInfo info{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; @@ -493,9 +485,6 @@ private: VkQueue queue_; int queueFamilyIndex_; - int curWidth_ = -1; - int curHeight_ = -1; - enum { MAX_BOUND_TEXTURES = 1, MAX_FRAME_COMMAND_BUFFERS = 256, @@ -1140,8 +1129,6 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { size_t vbBindOffset = push_->Push(vdata, vertexCount * curPipeline_->stride[0], &vulkanVbuf); uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); - VkBuffer buffers[1] = { vulkanVbuf }; - VkDeviceSize offsets[1] = { vbBindOffset }; VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf); renderManager_.BindPipeline(curPipeline_->vkpipeline); @@ -1253,20 +1240,19 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const { // use this frame's init command buffer. class VKFramebuffer : public Framebuffer { public: - VKFramebuffer(VulkanContext *vk, VKRFramebuffer *fb) : vulkan_(vk), buf_(fb) {} + VKFramebuffer(VKRFramebuffer *fb) : buf_(fb) {} ~VKFramebuffer() { delete buf_; } VKRFramebuffer *GetFB() const { return buf_; } private: VKRFramebuffer *buf_; - VulkanContext *vulkan_; }; Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) { VkCommandBuffer cmd = renderManager_.GetInitCmd(); VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetRenderPass(0), desc.width, desc.height); - VKFramebuffer *fb = new VKFramebuffer(vulkan_, vkrfb); + VKFramebuffer *fb = new VKFramebuffer(vkrfb); return fb; } @@ -1336,6 +1322,8 @@ void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void case Event::GOT_BACKBUFFER: renderManager_.CreateBackbuffers(); break; + default: + break; } // Noop } From 9b183f1f4be878d2f9b9daad285b874f8dda411e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 20:26:21 +0200 Subject: [PATCH 067/111] Forgot to add the CMakeLists.txt change to the last commit. --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3fc60adde..30acdec62a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -806,7 +806,9 @@ if(VULKAN) set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} ext/native/thin3d/thin3d_vulkan.cpp ext/native/thin3d/VulkanRenderManager.cpp - ext/native/thin3d/VulkanRenderManager.h) + ext/native/thin3d/VulkanRenderManager.h + ext/native/thin3d/VulkanQueueRunner.cpp + ext/native/thin3d/VulkanQueueRunner.h) endif() if(WIN32) set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} From 85cb604a2477e1e52832bbacb408811dd52c2e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 20:28:12 +0200 Subject: [PATCH 068/111] Fix (some) crashes on blits. Validation issues remain. --- GPU/Common/FramebufferCommon.cpp | 9 +++++++-- GPU/Common/FramebufferCommon.h | 6 +++++- GPU/GPUCommon.cpp | 2 ++ GPU/Vulkan/FramebufferVulkan.cpp | 3 +++ ext/native/thin3d/VulkanRenderManager.cpp | 21 ++++++++++++++++++--- ext/native/thin3d/VulkanRenderManager.h | 2 -- 6 files changed, 35 insertions(+), 8 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index c10f7ac653..f829829389 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -141,7 +141,7 @@ bool FramebufferManagerCommon::UpdateSize() { void FramebufferManagerCommon::BeginFrame() { DecimateFBOs(); - currentRenderVfb_ = 0; + currentRenderVfb_ = nullptr; updateVRAM_ = !(g_Config.iRenderingMode == FB_NON_BUFFERED_MODE || g_Config.iRenderingMode == FB_BUFFERED_MODE); } @@ -1043,6 +1043,11 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { void FramebufferManagerCommon::DecimateFBOs() { currentRenderVfb_ = 0; + for (auto iter : fbosToDelete_) { + delete iter; + } + fbosToDelete_.clear(); + for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; int age = frameLastFramebufUsed_ - std::max(vfb->last_frame_render, vfb->last_frame_used); @@ -1153,7 +1158,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min(vfb->bufferWidth, vfb->width), std::min(vfb->height, vfb->bufferHeight), 0); } } - delete old.fbo; + fbosToDelete_.push_back(old.fbo); if (needGLESRebinds_) { draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); } diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 2fd39d9a0d..62befe6913 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -190,7 +190,7 @@ public: virtual ~FramebufferManagerCommon(); virtual void Init(); - void BeginFrame(); + virtual void BeginFrame(); void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format); void DestroyFramebuf(VirtualFramebuffer *v); @@ -209,6 +209,8 @@ public: FramebufferHeuristicParams inputs; GetFramebufferHeuristicInputs(&inputs, gstate); VirtualFramebuffer *vfb = DoSetRenderFrameBuffer(inputs, skipDrawReason); + _dbg_assert_msg_(G3D, vfb, "DoSetRenderFramebuffer must return a valid framebuffer."); + _dbg_assert_msg_(G3D, currentRenderVfb_, "DoSetRenderFramebuffer must set a valid framebuffer."); return vfb; } } @@ -412,6 +414,8 @@ protected: std::map tempFBOs_; + std::vector fbosToDelete_; + // Aggressively delete unused FBOs to save gpu memory. enum { FBO_OLD_AGE = 5, diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 53b39e6d32..852c37b615 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1083,6 +1083,8 @@ void GPUCommon::ProcessEvent(GPUEvent ev) { break; case GPU_EVENT_COPY_DISPLAY_TO_OUTPUT: + // Ending the frame will unbind the framebuffer. Better finish any outstanding draw calls... + drawEngineCommon_->DispatchFlush(); CopyDisplayToOutputInternal(); break; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index f9c6946e97..5cf7ab2cbd 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -93,6 +93,9 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk vulkan2D_(vulkan) { InitDeviceObjects(); + + // After a blit we do need to rebind for the VulkanRenderManager to know what to do. + needGLESRebinds_ = true; } FramebufferManagerVulkan::~FramebufferManagerVulkan() { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 6a058eeed7..d8dd08ad3f 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -580,6 +580,9 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, step->copy.srcRect = srcRect; step->copy.dst = dst; step->copy.dstPos = dstPos; + + // TODO: Validate or clip copy-rectangles here. + std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; @@ -593,6 +596,9 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, step->blit.dst = dst; step->blit.dstRect = dstRect; step->blit.filter = filter; + + // TODO: Validate blit-rectangles here. + std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; @@ -985,6 +991,9 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; + default: + Crash(); + break; } TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, @@ -1009,6 +1018,9 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; + default: + Crash(); + break; } TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, fb->color.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, @@ -1130,9 +1142,8 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) VKRFramebuffer *src = step.blit.src; VKRFramebuffer *dst = step.blit.dst; - int srcCount = 0; - int dstCount = 0; - + // If any validation needs to be performed here, it should probably have been done + // already when the blit was queued. So don't validate here. VkImageBlit blit{}; blit.srcOffsets[0].x = step.blit.srcRect.offset.x; blit.srcOffsets[0].y = step.blit.srcRect.offset.y; @@ -1153,6 +1164,10 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) VkPipelineStageFlags srcStage = 0; VkPipelineStageFlags dstStage = 0; + + int srcCount = 0; + int dstCount = 0; + // First source barriers. if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 6f4ffb5bff..4f45e0e6d4 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -212,7 +212,6 @@ public: // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); - void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -364,7 +363,6 @@ private: int curHeight_; bool insideFrame_ = false; VKRStep *curRenderStep_; - VKRFramebuffer *boundFramebuffer_; std::vector steps_; // Execution time state From 12f858098432cc7a324fa8574742d9f21859524f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 21:19:42 +0200 Subject: [PATCH 069/111] Assorted fixes --- GPU/Common/FramebufferCommon.cpp | 9 +++-- ext/native/thin3d/VulkanRenderManager.cpp | 43 ++++++++++++++++++----- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index f829829389..3291f94754 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -307,7 +307,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame bool vfbFormatChanged = false; // Find a matching framebuffer - VirtualFramebuffer *vfb = 0; + VirtualFramebuffer *vfb = nullptr; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *v = vfbs_[i]; if (v->fb_address == params.fb_address) { @@ -1095,6 +1095,9 @@ void FramebufferManagerCommon::DecimateFBOs() { void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, u16 h, bool force, bool skipCopy) { VirtualFramebuffer old = *vfb; + int oldWidth = vfb->bufferWidth; + int oldHeight = vfb->bufferHeight; + if (force) { vfb->bufferWidth = w; vfb->bufferHeight = h; @@ -1151,11 +1154,11 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, (Draw::FBColorDepth)vfb->colorDepth }); if (old.fbo) { - INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %i x %i x %i", vfb->fb_address, w, h, vfb->format); + INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %d x %d x %d", vfb->fb_address, w, h, vfb->format); if (vfb->fbo) { draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); if (!skipCopy && !g_Config.bDisableSlowFramebufEffects) { - BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min(vfb->bufferWidth, vfb->width), std::min(vfb->height, vfb->bufferHeight), 0); + BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0); } } fbosToDelete_.push_back(old.fbo); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index d8dd08ad3f..558258c528 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -574,22 +574,42 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten } void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask) { + _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= src->height, "srcrect offset y + extent > height"); + _dbg_assert_msg_(G3D, dstPos.x >= 0, "dstPos offset x < 0"); + _dbg_assert_msg_(G3D, dstPos.y >= 0, "dstPos offset y < 0"); + _dbg_assert_msg_(G3D, dstPos.x + srcRect.extent.width <= dst->width, "dstPos + extent x > width"); + _dbg_assert_msg_(G3D, dstPos.y + srcRect.extent.height <= dst->height, "dstPos + extent y > height"); + VKRStep *step = new VKRStep{ VKRStepType::COPY }; + step->copy.aspectMask = aspectMask; step->copy.src = src; step->copy.srcRect = srcRect; step->copy.dst = dst; step->copy.dstPos = dstPos; - // TODO: Validate or clip copy-rectangles here. - std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; + curFramebuffer_ = nullptr; } void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { + _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= src->height, "srcrect offset y + extent > height"); + + _dbg_assert_msg_(G3D, dstRect.offset.x >= 0, "dstrect offset x < 0"); + _dbg_assert_msg_(G3D, dstRect.offset.y >= 0, "dstrect offset y < 0"); + _dbg_assert_msg_(G3D, dstRect.offset.x + dstRect.extent.width <= dst->width, "dstrect offset x + extent > width"); + _dbg_assert_msg_(G3D, dstRect.offset.y + dstRect.extent.height <= dst->height, "dstrect offset y + extent > height"); + VKRStep *step = new VKRStep{ VKRStepType::BLIT }; + step->blit.aspectMask = aspectMask; step->blit.src = src; step->blit.srcRect = srcRect; @@ -597,11 +617,10 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, step->blit.dstRect = dstRect; step->blit.filter = filter; - // TODO: Validate blit-rectangles here. - std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; + curFramebuffer_ = nullptr; } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { @@ -625,6 +644,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Flush() { curRenderStep_ = nullptr; + curFramebuffer_ = nullptr; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (frameData.hasInitCommands) { @@ -792,6 +812,7 @@ void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer } } + // This is supposed to bind a vulkan render pass to the command buffer. PerformBindFramebufferAsRenderTarget(step, cmd, swapChainImage); VKRFramebuffer *fb = step.render.framebuffer; @@ -936,6 +957,8 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st h = vulkan_->GetBackbufferHeight(); } +#if 0 + // This part is based on faulty old thinking. if (framebuf == curFramebuffer_) { if (framebuf == 0) Crash(); @@ -966,6 +989,7 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st // We're done. return; } +#endif VkRenderPass renderPass; int numClearVals = 0; @@ -1022,8 +1046,8 @@ void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &st Crash(); break; } - TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, - fb->color.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + TransitionImageLayout2(cmd, fb->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + fb->depth.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT); fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; @@ -1086,8 +1110,8 @@ void VulkanRenderManager::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) int srcCount = 0; int dstCount = 0; - VkPipelineStageFlags srcStage; - VkPipelineStageFlags dstStage; + VkPipelineStageFlags srcStage = 0; + VkPipelineStageFlags dstStage = 0; // First source barriers. if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { @@ -1200,6 +1224,9 @@ void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, step.blit.filter); } + + // TODO: Need to check if the depth format is blittable. + // Actually, we should probably almost always use copies rather than blits for depth buffers. if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { blit.srcSubresource.aspectMask = 0; blit.dstSubresource.aspectMask = 0; From 613cc46285f12400d67b1b3dd6aacbfd051f83ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 21:56:39 +0200 Subject: [PATCH 070/111] Remove a bad check making us use the wrong way to copy depth buffers. Add a cap so we can try to unify BlitFramebufferDepth later. --- GPU/Vulkan/FramebufferVulkan.cpp | 6 ++++-- ext/native/thin3d/thin3d.h | 2 ++ ext/native/thin3d/thin3d_d3d11.cpp | 3 +++ ext/native/thin3d/thin3d_d3d9.cpp | 2 ++ ext/native/thin3d/thin3d_gl.cpp | 1 + ext/native/thin3d/thin3d_vulkan.cpp | 5 ++++- 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 5cf7ab2cbd..d6124cc467 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -434,13 +434,15 @@ void FramebufferManagerVulkan::BlitFramebufferDepth(VirtualFramebuffer *src, Vir bool matchingDepthBuffer = src->z_address == dst->z_address && src->z_stride != 0 && dst->z_stride != 0; bool matchingSize = src->width == dst->width && src->height == dst->height; bool matchingRenderSize = src->renderWidth == dst->renderWidth && src->renderHeight == dst->renderHeight; - - if (gstate_c.Supports(GPU_SUPPORTS_ANY_COPY_IMAGE) && matchingDepthBuffer && matchingRenderSize && matchingSize) { + if (matchingDepthBuffer && matchingRenderSize && matchingSize) { + // TODO: Currently, this copies depth AND stencil, which is a problem. See #9740. draw_->CopyFramebufferImage(src->fbo, 0, 0, 0, 0, dst->fbo, 0, 0, 0, 0, src->renderWidth, src->renderHeight, 1, Draw::FB_DEPTH_BIT); } else if (matchingDepthBuffer && matchingSize) { + /* int w = std::min(src->renderWidth, dst->renderWidth); int h = std::min(src->renderHeight, dst->renderHeight); draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST); + */ } } diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index fdb68a3849..6b3a6dfac9 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -555,6 +555,8 @@ struct DeviceCaps { bool logicOpSupported; bool framebufferCopySupported; bool framebufferBlitSupported; + bool framebufferDepthCopySupported; + bool framebufferDepthBlitSupported; }; struct TextureDesc { diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index e7ca762369..a40d4c4bfc 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -217,6 +217,9 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de caps_.depthRangeMinusOneToOne = false; caps_.framebufferBlitSupported = false; + caps_.framebufferCopySupported = true; + caps_.framebufferDepthBlitSupported = false; + caps_.framebufferDepthCopySupported = true; D3D11_FEATURE_DATA_D3D11_OPTIONS options{}; HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options)); diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 118bd48fab..3ac3058575 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -601,6 +601,8 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID caps_.tesselationShaderSupported = false; caps_.framebufferBlitSupported = true; caps_.framebufferCopySupported = false; + caps_.framebufferDepthBlitSupported = true; + caps_.framebufferDepthCopySupported = false; if (d3d) { D3DDISPLAYMODE displayMode; d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode); diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 1334a26e2c..36532fabe9 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -602,6 +602,7 @@ OpenGLContext::OpenGLContext() { caps_.preferredDepthBufferFormat = DataFormat::D24_S8; } caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object; + caps_.framebufferDepthBlitSupported = caps_.framebufferBlitSupported; } OpenGLContext::~OpenGLContext() { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 5a371da48f..005523b991 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -658,7 +658,10 @@ VKContext::VKContext(VulkanContext *vulkan) caps_.dualSourceBlend = vulkan->GetFeaturesAvailable().dualSrcBlend != 0; caps_.framebufferBlitSupported = true; caps_.framebufferCopySupported = true; - caps_.preferredDepthBufferFormat = DataFormat::D24_S8; + caps_.framebufferDepthBlitSupported = false; // Can be checked for. + caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case. + caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan. + device_ = vulkan->GetDevice(); queue_ = vulkan->GetGraphicsQueue(); From 54e4eab1c153a83134acf18a865e32eb7caf6f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 22:04:25 +0200 Subject: [PATCH 071/111] Warning fixes --- ext/native/thin3d/VulkanRenderManager.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 558258c528..90881a303e 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -576,12 +576,12 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask) { _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); - _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= src->width, "srcrect offset x + extent > width"); - _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= src->height, "srcrect offset y + extent > height"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= (uint32_t)src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= (uint32_t)src->height, "srcrect offset y + extent > height"); _dbg_assert_msg_(G3D, dstPos.x >= 0, "dstPos offset x < 0"); _dbg_assert_msg_(G3D, dstPos.y >= 0, "dstPos offset y < 0"); - _dbg_assert_msg_(G3D, dstPos.x + srcRect.extent.width <= dst->width, "dstPos + extent x > width"); - _dbg_assert_msg_(G3D, dstPos.y + srcRect.extent.height <= dst->height, "dstPos + extent y > height"); + _dbg_assert_msg_(G3D, dstPos.x + srcRect.extent.width <= (uint32_t)dst->width, "dstPos + extent x > width"); + _dbg_assert_msg_(G3D, dstPos.y + srcRect.extent.height <= (uint32_t)dst->height, "dstPos + extent y > height"); VKRStep *step = new VKRStep{ VKRStepType::COPY }; @@ -600,13 +600,13 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { _dbg_assert_msg_(G3D, srcRect.offset.x >= 0, "srcrect offset x < 0"); _dbg_assert_msg_(G3D, srcRect.offset.y >= 0, "srcrect offset y < 0"); - _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= src->width, "srcrect offset x + extent > width"); - _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= src->height, "srcrect offset y + extent > height"); + _dbg_assert_msg_(G3D, srcRect.offset.x + srcRect.extent.width <= (uint32_t)src->width, "srcrect offset x + extent > width"); + _dbg_assert_msg_(G3D, srcRect.offset.y + srcRect.extent.height <= (uint32_t)src->height, "srcrect offset y + extent > height"); _dbg_assert_msg_(G3D, dstRect.offset.x >= 0, "dstrect offset x < 0"); _dbg_assert_msg_(G3D, dstRect.offset.y >= 0, "dstrect offset y < 0"); - _dbg_assert_msg_(G3D, dstRect.offset.x + dstRect.extent.width <= dst->width, "dstrect offset x + extent > width"); - _dbg_assert_msg_(G3D, dstRect.offset.y + dstRect.extent.height <= dst->height, "dstrect offset y + extent > height"); + _dbg_assert_msg_(G3D, dstRect.offset.x + dstRect.extent.width <= (uint32_t)dst->width, "dstrect offset x + extent > width"); + _dbg_assert_msg_(G3D, dstRect.offset.y + dstRect.extent.height <= (uint32_t)dst->height, "dstrect offset y + extent > height"); VKRStep *step = new VKRStep{ VKRStepType::BLIT }; From 8644e85d3da990180d6a80d4518f2cdf6c052916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 25 Oct 2017 22:09:08 +0200 Subject: [PATCH 072/111] Remove misguided hack. --- GPU/GPUCommon.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 852c37b615..53b39e6d32 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1083,8 +1083,6 @@ void GPUCommon::ProcessEvent(GPUEvent ev) { break; case GPU_EVENT_COPY_DISPLAY_TO_OUTPUT: - // Ending the frame will unbind the framebuffer. Better finish any outstanding draw calls... - drawEngineCommon_->DispatchFlush(); CopyDisplayToOutputInternal(); break; From 07e8b4ff1aefdf5108dc21d20d1f109f6eedc8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 00:55:09 +0200 Subject: [PATCH 073/111] Bump descriptor set limits, which became insufficient with the addition of tesselation (should really use separate big desc layouts for them) --- GPU/Vulkan/DrawEngineVulkan.cpp | 10 ++++++---- ext/native/thin3d/VulkanRenderManager.cpp | 6 +++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 3d326d7556..80245e4282 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -126,7 +126,8 @@ void DrawEngineVulkan::InitDeviceObjects() { bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; bindings[4].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; bindings[4].binding = DRAW_BINDING_DYNUBO_BONE; - // Hardware tessellation + // Hardware tessellation. TODO: Don't allocate these unless actually drawing splines. + // Will require additional bindings[5].descriptorCount = 1; bindings[5].pImmutableSamplers = nullptr; bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -152,9 +153,9 @@ void DrawEngineVulkan::InitDeviceObjects() { assert(VK_SUCCESS == res); VkDescriptorPoolSize dpTypes[2]; - dpTypes[0].descriptorCount = 4096; + dpTypes[0].descriptorCount = 8192; dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - dpTypes[1].descriptorCount = 2048; + dpTypes[1].descriptorCount = 8192 + 4096; // Due to the tess stuff, we need a LOT of these. Most will be empty... dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; @@ -536,7 +537,8 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView descAlloc.descriptorPool = frame->descPool; descAlloc.descriptorSetCount = 1; VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc); - assert(result == VK_SUCCESS); + // Even in release mode, this is bad. + _assert_msg_(G3D, result == VK_SUCCESS, "Ran out of descriptors in pool. sz=%d", (int)frame->descSets.size()); // We just don't write to the slots we don't care about. VkWriteDescriptorSet writes[7]; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 90881a303e..a6930938b3 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -758,7 +758,11 @@ void VulkanRenderManager::Run(int frame) { res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI // return codes - assert(res == VK_SUCCESS); + if (res == VK_ERROR_OUT_OF_DATE_KHR) { + // ignore, it'll be fine. this happens sometimes during resizes, and we do make sure to recreate the swap chain. + } else { + assert(res == VK_SUCCESS); + } VLOG("PULL: Finished running frame %d", frame); } From ecad09d18f060102666483eede7854db52d9af18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 01:17:10 +0200 Subject: [PATCH 074/111] 32-bit android buildfix attempt --- ext/native/thin3d/VulkanRenderManager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index a6930938b3..f6e8699ab0 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -1,3 +1,4 @@ +#include #include "base/logging.h" #include "Common/Vulkan/VulkanContext.h" @@ -594,7 +595,7 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; - curFramebuffer_ = nullptr; + curFramebuffer_ = VK_NULL_HANDLE; } void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { @@ -620,7 +621,7 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; - curFramebuffer_ = nullptr; + curFramebuffer_ = VK_NULL_HANDLE; } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { @@ -644,7 +645,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Flush() { curRenderStep_ = nullptr; - curFramebuffer_ = nullptr; + curFramebuffer_ = VK_N; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (frameData.hasInitCommands) { From 73125762396fc4246a77fd25cfd4f451f96ce056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 11:33:52 +0200 Subject: [PATCH 075/111] More buildfixes, warning fix, memory leak fix --- GPU/D3D11/GPU_D3D11.cpp | 3 ++- GPU/Directx9/GPU_DX9.cpp | 2 +- GPU/Vulkan/DrawEngineVulkan.cpp | 2 +- ext/native/thin3d/VulkanRenderManager.cpp | 11 +++++++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 95dadcf07d..cc3f06f16e 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -317,12 +317,13 @@ void GPU_D3D11::EndHostFrame() { } void GPU_D3D11::BeginFrameInternal() { + GPUCommon::BeginFrameInternal(); + textureCacheD3D11_->StartFrame(); drawEngine_.BeginFrame(); depalShaderCache_->Decimate(); // fragmentTestCache_.Decimate(); - GPUCommon::BeginFrameInternal(); shaderManagerD3D11_->DirtyLastShader(); framebufferManagerD3D11_->BeginFrame(); diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 3221e487a9..0dd93e7137 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -295,7 +295,7 @@ void GPU_DX9::BeginFrameInternal() { GPUCommon::BeginFrameInternal(); shaderManagerDX9_->DirtyShader(); - framebufferManagerDX9_->BeginFrame(); + framebufferManager_->BeginFrame(); } void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) { diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 80245e4282..537dfdc756 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -242,7 +242,7 @@ void DrawEngineVulkan::FrameData::Destroy(VulkanContext *vulkan) { } void DrawEngineVulkan::DestroyDeviceObjects() { - for (int i = 0; i < 2; i++) { + for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].Destroy(vulkan_); } if (depalSampler_ != VK_NULL_HANDLE) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index f6e8699ab0..daa07812cb 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -60,12 +60,12 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.imageView); assert(res == VK_SUCCESS); - VkPipelineStageFlagBits dstStage; + VkPipelineStageFlags dstStage; VkAccessFlagBits dstAccessMask; switch (initialLayout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; @@ -73,7 +73,10 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dstStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + default: + Crash(); break; } @@ -645,7 +648,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Flush() { curRenderStep_ = nullptr; - curFramebuffer_ = VK_N; + curFramebuffer_ = VK_NULL_HANDLE; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (frameData.hasInitCommands) { From 5456194a696283bc325479e886d30b301c0b2feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 12:28:36 +0200 Subject: [PATCH 076/111] Fix integer overflow affecting very large draw calls --- ext/native/thin3d/VulkanRenderManager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 4f45e0e6d4..780481f669 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -39,7 +39,7 @@ struct VkRenderData { uint32_t uboOffsets[3]; VkBuffer vbuffer; VkDeviceSize voffset; - int count; + uint32_t count; } draw; struct { VkPipelineLayout pipelineLayout; @@ -50,7 +50,7 @@ struct VkRenderData { VkDeviceSize voffset; VkBuffer ibuffer; VkDeviceSize ioffset; - int16_t count; + uint32_t count; int16_t instances; VkIndexType indexType; } drawIndexed; From 1e9984dbf688070aa79a538e85897d883e21e2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 26 Oct 2017 13:00:27 +0200 Subject: [PATCH 077/111] Another buildfix --- ext/native/thin3d/VulkanRenderManager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index daa07812cb..2550c2b6fc 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -14,6 +14,11 @@ // TODO: Using a thread here is unfinished and does not work correctly. const bool useThread = false; +#ifndef UINT64_MAX +#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL +#endif + + void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) { VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; ici.arrayLayers = 1; @@ -26,12 +31,14 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int ici.samples = VK_SAMPLE_COUNT_1_BIT; ici.tiling = VK_IMAGE_TILING_OPTIMAL; ici.format = format; + // Strictly speaking we don't yet need VK_IMAGE_USAGE_SAMPLED_BIT for depth buffers since we do not yet sample depth buffers. ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; if (color) { ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; } else { ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } + vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image); // TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers From fa81dd9c448bd711ed3d3ceb432d792a9b71c28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 27 Oct 2017 01:25:41 +0200 Subject: [PATCH 078/111] Forgot to update the Android target version here to 26. --- android/project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/project.properties b/android/project.properties index 8b8e6098e7..5e19b7a0b1 100644 --- a/android/project.properties +++ b/android/project.properties @@ -9,4 +9,4 @@ # # Project target. -target=android-25 +target=android-26 From 3f918ed32835ceac3fd990289fa836bbf42b42ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 27 Oct 2017 22:10:36 +0200 Subject: [PATCH 079/111] Refactor: Split out VulkanQueueRunner from VulkanRenderManager Should be no functionality change, but good to do before adding more functionality. --- ext/native/thin3d/VulkanQueueRunner.cpp | 689 ++++++++++++++++++++ ext/native/thin3d/VulkanQueueRunner.h | 176 ++++++ ext/native/thin3d/VulkanRenderManager.cpp | 733 +--------------------- ext/native/thin3d/VulkanRenderManager.h | 168 +---- ext/native/thin3d/thin3d_vulkan.cpp | 6 +- 5 files changed, 891 insertions(+), 881 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index ad86d0bcec..c101c563bf 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -1 +1,690 @@ #include "VulkanQueueRunner.h" +#include "VulkanRenderManager.h" + +void VulkanQueueRunner::CreateDeviceObjects() { + InitBackbufferRenderPass(); + InitRenderpasses(); +} + +void VulkanQueueRunner::DestroyDeviceObjects() { + VkDevice device = vulkan_->GetDevice(); + for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { + assert(renderPasses_[i] != VK_NULL_HANDLE); + vkDestroyRenderPass(device, renderPasses_[i], nullptr); + } + assert(backbufferRenderPass_ != VK_NULL_HANDLE); + vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); +} + + +void VulkanQueueRunner::InitBackbufferRenderPass() { + VkResult U_ASSERT_ONLY res; + + VkAttachmentDescription attachments[2]; + attachments[0].format = vulkan_->GetSwapchainFormat(); + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].flags = 0; + + attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; // must use this same format later for the back depth buffer. + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].flags = 0; + + VkAttachmentReference color_reference = {}; + color_reference.attachment = 0; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depth_reference{}; + depth_reference.attachment = 1; + depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = nullptr; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + subpass.pResolveAttachments = nullptr; + subpass.pDepthStencilAttachment = &depth_reference; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = nullptr; + + VkRenderPassCreateInfo rp_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + rp_info.pNext = nullptr; + rp_info.attachmentCount = 2; + rp_info.pAttachments = attachments; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 0; + rp_info.pDependencies = nullptr; + + res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, nullptr, &backbufferRenderPass_); + assert(res == VK_SUCCESS); +} + +void VulkanQueueRunner::InitRenderpasses() { + // Create a bunch of render pass objects, for normal rendering with a depth buffer, + // with clearing, without clearing, and dont-care for both depth/stencil and color, so 3*3=9 combos. + VkAttachmentDescription attachments[2] = {}; + attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].flags = 0; + + attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].flags = 0; + + VkAttachmentReference color_reference = {}; + color_reference.attachment = 0; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depth_reference = {}; + depth_reference.attachment = 1; + depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = nullptr; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + subpass.pResolveAttachments = nullptr; + subpass.pDepthStencilAttachment = &depth_reference; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = nullptr; + + VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + rp.attachmentCount = 2; + rp.pAttachments = attachments; + rp.subpassCount = 1; + rp.pSubpasses = &subpass; + rp.dependencyCount = 0; + rp.pDependencies = nullptr; + + for (int depth = 0; depth < 3; depth++) { + switch ((VKRRenderPassAction)depth) { + case VKRRenderPassAction::CLEAR: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + break; + case VKRRenderPassAction::KEEP: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + break; + case VKRRenderPassAction::DONT_CARE: + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + break; + } + for (int color = 0; color < 3; color++) { + switch ((VKRRenderPassAction)color) { + case VKRRenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; + case VKRRenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; + case VKRRenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; + } + int index = RPIndex((VKRRenderPassAction)color, (VKRRenderPassAction)depth); + vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[index]); + } + } +} +void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector &steps) { + // Optimizes renderpasses, then sequences them. + for (int i = 0; i < steps.size(); i++) { + const VKRStep &step = *steps[i]; + switch (step.stepType) { + case VKRStepType::RENDER: + PerformRenderPass(step, cmd); + break; + case VKRStepType::COPY: + PerformCopy(step, cmd); + break; + case VKRStepType::BLIT: + PerformBlit(step, cmd); + break; + case VKRStepType::READBACK: + // PerformReadback + break; + } + delete steps[i]; + } +} + +void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { + // TODO: If there are multiple, we can transition them together. + for (const auto &iter : step.preTransitions) { + if (iter.fb->color.layout != iter.targetLayout) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = iter.fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = iter.fb->color.image; + barrier.srcAccessMask = 0; + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; + switch (barrier.oldLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + default: + Crash(); + break; + } + barrier.newLayout = iter.targetLayout; + switch (barrier.newLayout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + default: + Crash(); + break; + } + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier(cmd, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + iter.fb->color.layout = barrier.newLayout; + } + } + + // This is supposed to bind a vulkan render pass to the command buffer. + PerformBindFramebufferAsRenderTarget(step, cmd); + + int curWidth = step.render.framebuffer ? step.render.framebuffer->width : vulkan_->GetBackbufferWidth(); + int curHeight = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight(); + + VKRFramebuffer *fb = step.render.framebuffer; + + VkPipeline lastPipeline = VK_NULL_HANDLE; + + auto &commands = step.commands; + + // TODO: Dynamic state commands (SetViewport, SetScissor, SetBlendConstants, SetStencil*) are only + // valid when a pipeline is bound with those as dynamic state. So we need to add some state tracking here + // for this to be correct. This is a bit of a pain but also will let us eliminate redundant calls. + + for (const auto &c : commands) { + switch (c.cmd) { + case VKRRenderCommand::BIND_PIPELINE: + if (c.pipeline.pipeline != lastPipeline) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.pipeline.pipeline); + lastPipeline = c.pipeline.pipeline; + } + break; + + case VKRRenderCommand::VIEWPORT: + vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp); + break; + + case VKRRenderCommand::SCISSOR: + vkCmdSetScissor(cmd, 0, 1, &c.scissor.scissor); + break; + + case VKRRenderCommand::BLEND: + vkCmdSetBlendConstants(cmd, c.blendColor.color); + break; + + case VKRRenderCommand::STENCIL: + vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); + vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); + vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); + break; + + case VKRRenderCommand::DRAW_INDEXED: + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); + vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); + vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); + vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); + break; + + case VKRRenderCommand::DRAW: + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); + vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); + vkCmdDraw(cmd, c.draw.count, 1, 0, 0); + break; + + case VKRRenderCommand::CLEAR: + { + int numAttachments = 0; + VkClearRect rc{}; + rc.baseArrayLayer = 0; + rc.layerCount = 1; + rc.rect.extent.width = curWidth; + rc.rect.extent.height = curHeight; + VkClearAttachment attachments[2]; + if (c.clear.clearMask & VK_IMAGE_ASPECT_COLOR_BIT) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + attachment.colorAttachment = 0; + Uint8x4ToFloat4(attachment.clearValue.color.float32, c.clear.clearColor); + } + if (c.clear.clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + VkClearAttachment &attachment = attachments[numAttachments++]; + attachment.aspectMask = 0; + if (c.clear.clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + attachment.clearValue.depthStencil.depth = c.clear.clearZ; + attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (c.clear.clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + attachment.clearValue.depthStencil.stencil = c.clear.clearStencil; + attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } + if (numAttachments) { + vkCmdClearAttachments(cmd, numAttachments, attachments, 1, &rc); + } + break; + } + default: + ELOG("Unimpl queue command"); + ; + } + } + vkCmdEndRenderPass(cmd); + + // Transition the framebuffer if requested. + if (fb && step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED) { + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = fb->color.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = fb->color.image; + barrier.srcAccessMask = 0; + switch (barrier.oldLayout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + break; + default: + Crash(); + } + barrier.newLayout = step.render.finalColorLayout; + switch (barrier.newLayout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + break; + default: + Crash(); + } + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + + // we're between passes so it's OK. + // ARM Best Practices guide recommends these stage bits. + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + fb->color.layout = barrier.newLayout; + } +} + +void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) { + VkFramebuffer framebuf; + int w; + int h; + VkImageLayout prevLayout; + if (step.render.framebuffer) { + VKRFramebuffer *fb = step.render.framebuffer; + framebuf = fb->framebuf; + w = fb->width; + h = fb->height; + prevLayout = fb->color.layout; + } else { + framebuf = backbuffer_; + w = vulkan_->GetBackbufferWidth(); + h = vulkan_->GetBackbufferHeight(); + } + + VkRenderPass renderPass; + int numClearVals = 0; + VkClearValue clearVal[2]; + memset(clearVal, 0, sizeof(clearVal)); + if (step.render.framebuffer) { + VKRFramebuffer *fb = step.render.framebuffer; + // Now, if the image needs transitioning, let's transition. + // The backbuffer does not, that's handled by VulkanContext. + if (step.render.framebuffer->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; + switch (fb->color.layout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + default: + Crash(); + break; + } + + TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, + fb->color.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT); + fb->color.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + VkAccessFlags srcAccessMask; + VkPipelineStageFlags srcStage; + switch (fb->depth.layout) { + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + default: + Crash(); + break; + } + TransitionImageLayout2(cmd, fb->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + fb->depth.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT); + fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; + // VLOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); + if (step.render.color == VKRRenderPassAction::CLEAR) { + Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); + numClearVals = 1; + } + if (step.render.depthStencil == VKRRenderPassAction::CLEAR) { + clearVal[1].depthStencil.depth = step.render.clearDepth; + clearVal[1].depthStencil.stencil = step.render.clearStencil; + numClearVals = 2; + } + } else { + renderPass = GetBackbufferRenderPass(); + numClearVals = 2; // We don't bother with a depth buffer here. + clearVal[1].depthStencil.depth = 0.0f; + clearVal[1].depthStencil.stencil = 0; + } + + VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + rp_begin.renderPass = renderPass; + rp_begin.framebuffer = framebuf; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = w; + rp_begin.renderArea.extent.height = h; + rp_begin.clearValueCount = numClearVals; + rp_begin.pClearValues = numClearVals ? clearVal : nullptr; + vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); +} + +void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { + VKRFramebuffer *src = step.copy.src; + VKRFramebuffer *dst = step.copy.dst; + + VkImageCopy copy{}; + copy.srcOffset.x = step.copy.srcRect.offset.x; + copy.srcOffset.y = step.copy.srcRect.offset.y; + copy.srcOffset.z = 0; + copy.srcSubresource.mipLevel = 0; + copy.srcSubresource.layerCount = 1; + copy.dstOffset.x = step.copy.dstPos.x; + copy.dstOffset.y = step.copy.dstPos.y; + copy.dstOffset.z = 0; + copy.dstSubresource.mipLevel = 0; + copy.dstSubresource.layerCount = 1; + copy.extent.width = step.copy.srcRect.extent.width; + copy.extent.height = step.copy.srcRect.extent.height; + copy.extent.depth = 1; + + VkImageMemoryBarrier srcBarriers[2]{}; + VkImageMemoryBarrier dstBarriers[2]{}; + int srcCount = 0; + int dstCount = 0; + + VkPipelineStageFlags srcStage = 0; + VkPipelineStageFlags dstStage = 0; + // First source barriers. + if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); + } + if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + // We can't copy only depth or only stencil unfortunately. + if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + } + + if (srcCount) { + vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + } + if (dstCount) { + vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + } + + if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©); + } + if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + copy.srcSubresource.aspectMask = 0; + copy.dstSubresource.aspectMask = 0; + if (step.copy.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (step.copy.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©); + } +} + +void VulkanQueueRunner::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) { + VkImageMemoryBarrier srcBarriers[2]{}; + VkImageMemoryBarrier dstBarriers[2]{}; + + VKRFramebuffer *src = step.blit.src; + VKRFramebuffer *dst = step.blit.dst; + + // If any validation needs to be performed here, it should probably have been done + // already when the blit was queued. So don't validate here. + VkImageBlit blit{}; + blit.srcOffsets[0].x = step.blit.srcRect.offset.x; + blit.srcOffsets[0].y = step.blit.srcRect.offset.y; + blit.srcOffsets[0].z = 0; + blit.srcOffsets[1].x = step.blit.srcRect.offset.x + step.blit.srcRect.extent.width; + blit.srcOffsets[1].y = step.blit.srcRect.offset.y + step.blit.srcRect.extent.height; + blit.srcOffsets[1].z = 1; + blit.srcSubresource.mipLevel = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0].x = step.blit.dstRect.offset.x; + blit.dstOffsets[0].y = step.blit.dstRect.offset.y; + blit.dstOffsets[0].z = 0; + blit.dstOffsets[1].x = step.blit.dstRect.offset.x + step.blit.dstRect.extent.width; + blit.dstOffsets[1].y = step.blit.dstRect.offset.y + step.blit.dstRect.extent.height; + blit.dstOffsets[1].z = 1; + blit.dstSubresource.mipLevel = 0; + blit.dstSubresource.layerCount = 1; + + VkPipelineStageFlags srcStage = 0; + VkPipelineStageFlags dstStage = 0; + + int srcCount = 0; + int dstCount = 0; + + // First source barriers. + if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); + } + if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + // We can't copy only depth or only stencil unfortunately. + if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + } + } + + if (srcCount) { + vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); + } + if (dstCount) { + vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); + } + + if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, step.blit.filter); + } + + // TODO: Need to check if the depth format is blittable. + // Actually, we should probably almost always use copies rather than blits for depth buffers. + if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + blit.srcSubresource.aspectMask = 0; + blit.dstSubresource.aspectMask = 0; + if (step.blit.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { + blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + } + if (step.blit.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { + blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + vkCmdBlitImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &blit, step.blit.filter); + } +} + +void VulkanQueueRunner::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = img.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = img.image; + barrier.srcAccessMask = 0; + switch (img.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + default: + Crash(); + } + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + img.layout = barrier.newLayout; +} + +void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = img.layout; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + barrier.image = img.image; + barrier.srcAccessMask = 0; + switch (img.layout) { + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + default: + Crash(); + } + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.subresourceRange.aspectMask = aspect; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + img.layout = barrier.newLayout; +} diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 9da875a95c..e72c986325 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -1,6 +1,182 @@ #pragma once +#include + +#include "Common/Vulkan/VulkanContext.h" +#include "math/dataconv.h" +#include "thin3d/thin3d.h" + +class VKRFramebuffer; +struct VKRImage; + +// The cool thing is that you can Flush on a different thread than you record the commands on! + +enum class VKRRenderCommand : uint8_t { + BIND_PIPELINE, + STENCIL, + BLEND, + VIEWPORT, + SCISSOR, + CLEAR, + DRAW, + DRAW_INDEXED, +}; + +struct VkRenderData { + VKRRenderCommand cmd; + union { + struct { + VkPipeline pipeline; + } pipeline; + struct { + VkPipelineLayout pipelineLayout; + VkDescriptorSet ds; + int numUboOffsets; + uint32_t uboOffsets[3]; + VkBuffer vbuffer; + VkDeviceSize voffset; + uint32_t count; + } draw; + struct { + VkPipelineLayout pipelineLayout; + VkDescriptorSet ds; + int numUboOffsets; + uint32_t uboOffsets[3]; + VkBuffer vbuffer; // might need to increase at some point + VkDeviceSize voffset; + VkBuffer ibuffer; + VkDeviceSize ioffset; + uint32_t count; + int16_t instances; + VkIndexType indexType; + } drawIndexed; + struct { + uint32_t clearColor; + float clearZ; + int clearStencil; + int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc + } clear; + struct { + VkViewport vp; + } viewport; + struct { + VkRect2D scissor; + } scissor; + struct { + uint8_t stencilWriteMask; + uint8_t stencilCompareMask; + uint8_t stencilRef; + } stencil; + struct { + float color[4]; + } blendColor; + struct { + + } beginRp; + struct { + + } endRp; + }; +}; + +enum class VKRStepType : uint8_t { + RENDER, + COPY, + BLIT, + READBACK, +}; + +enum class VKRRenderPassAction { + DONT_CARE, + CLEAR, + KEEP, +}; + +struct TransitionRequest { + VKRFramebuffer *fb; + VkImageLayout targetLayout; +}; + +struct VKRStep { + VKRStep(VKRStepType _type) : stepType(_type) {} + VKRStepType stepType; + std::vector commands; + std::vector preTransitions; + union { + struct { + VKRFramebuffer *framebuffer; + VKRRenderPassAction color; + VKRRenderPassAction depthStencil; + uint32_t clearColor; + float clearDepth; + int clearStencil; + int numDraws; + VkImageLayout finalColorLayout; + } render; + struct { + VKRFramebuffer *src; + VKRFramebuffer *dst; + VkRect2D srcRect; + VkOffset2D dstPos; + int aspectMask; + } copy; + struct { + VKRFramebuffer *src; + VKRFramebuffer *dst; + VkRect2D srcRect; + VkRect2D dstRect; + int aspectMask; + VkFilter filter; + } blit; + struct { + VKRFramebuffer *src; + void *destPtr; + VkRect2D srcRect; + } readback; + }; +}; + class VulkanQueueRunner { public: + VulkanQueueRunner(VulkanContext *vulkan) : vulkan_(vulkan) {} + void SetBackbuffer(VkFramebuffer fb) { + backbuffer_ = fb; + } + void RunSteps(VkCommandBuffer cmd, const std::vector &steps); + void CreateDeviceObjects(); + void DestroyDeviceObjects(); + + VkRenderPass GetBackbufferRenderPass() const { + return backbufferRenderPass_; + } + VkRenderPass GetRenderPass(int i) const { + return renderPasses_[i]; + } + + inline int RPIndex(VKRRenderPassAction color, VKRRenderPassAction depth) { + return (int)depth * 3 + (int)color; + } + +private: + void InitBackbufferRenderPass(); + void InitRenderpasses(); + + void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd); + void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd); + void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd); + void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); + + static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); + static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); + + VulkanContext *vulkan_; + + VkFramebuffer backbuffer_; + VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; + + VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; + // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. + // TODO: Create these on demand. + VkRenderPass renderPasses_[9]{}; }; \ No newline at end of file diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 2550c2b6fc..77b9f5f473 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -94,7 +94,7 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int img.layout = initialLayout; } -VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan) { +VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan), queueRunner_(vulkan) { VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; semaphoreCreateInfo.flags = 0; VkResult res = vkCreateSemaphore(vulkan_->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore_); @@ -124,8 +124,7 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan frameData_[i].fence = vulkan_->CreateFence(true); // So it can be instantly waited on } - InitBackbufferRenderPass(); - InitRenderpasses(); + queueRunner_.CreateDeviceObjects(); } void VulkanRenderManager::CreateBackbuffers() { @@ -229,12 +228,7 @@ VulkanRenderManager::~VulkanRenderManager() { vkDestroyCommandPool(device, frameData_[i].cmdPoolMain, nullptr); vkDestroyFence(device, frameData_[i].fence, nullptr); } - for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { - assert(renderPasses_[i] != VK_NULL_HANDLE); - vkDestroyRenderPass(device, renderPasses_[i], nullptr); - } - assert(backbufferRenderPass_ != VK_NULL_HANDLE); - vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); + queueRunner_.DestroyDeviceObjects(); } // TODO: Activate this code. @@ -340,7 +334,7 @@ void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { VLOG("InitFramebuffers: %dx%d", width, height); VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - fb_info.renderPass = backbufferRenderPass_; + fb_info.renderPass = queueRunner_.GetBackbufferRenderPass(); fb_info.attachmentCount = 2; fb_info.pAttachments = attachments; fb_info.width = width; @@ -356,63 +350,6 @@ void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { } } -void VulkanRenderManager::InitBackbufferRenderPass() { - VkResult U_ASSERT_ONLY res; - - VkAttachmentDescription attachments[2]; - attachments[0].format = vulkan_->GetSwapchainFormat(); - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].flags = 0; - - attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; // must use this same format later for the back depth buffer. - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].flags = 0; - - VkAttachmentReference color_reference = {}; - color_reference.attachment = 0; - color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depth_reference{}; - depth_reference.attachment = 1; - depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.flags = 0; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = nullptr; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_reference; - subpass.pResolveAttachments = nullptr; - subpass.pDepthStencilAttachment = &depth_reference; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - - VkRenderPassCreateInfo rp_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rp_info.pNext = nullptr; - rp_info.attachmentCount = 2; - rp_info.pAttachments = attachments; - rp_info.subpassCount = 1; - rp_info.pSubpasses = &subpass; - rp_info.dependencyCount = 0; - rp_info.pDependencies = nullptr; - - res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, nullptr, &backbufferRenderPass_); - assert(res == VK_SUCCESS); -} - void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { VkResult U_ASSERT_ONLY res; bool U_ASSERT_ONLY pass; @@ -486,85 +423,6 @@ void VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { assert(res == VK_SUCCESS); } -void VulkanRenderManager::InitRenderpasses() { - // Create a bunch of render pass objects, for normal rendering with a depth buffer, - // with clearing, without clearing, and dont-care for both depth/stencil and color, so 3*3=9 combos. - VkAttachmentDescription attachments[2] = {}; - attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].flags = 0; - - attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachments[1].flags = 0; - - VkAttachmentReference color_reference = {}; - color_reference.attachment = 0; - color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depth_reference = {}; - depth_reference.attachment = 1; - depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.flags = 0; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = nullptr; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_reference; - subpass.pResolveAttachments = nullptr; - subpass.pDepthStencilAttachment = &depth_reference; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = nullptr; - - VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rp.attachmentCount = 2; - rp.pAttachments = attachments; - rp.subpassCount = 1; - rp.pSubpasses = &subpass; - rp.dependencyCount = 0; - rp.pDependencies = nullptr; - - for (int depth = 0; depth < 3; depth++) { - switch ((VKRRenderPassAction)depth) { - case VKRRenderPassAction::CLEAR: - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - break; - case VKRRenderPassAction::KEEP: - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - break; - case VKRRenderPassAction::DONT_CARE: - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - break; - } - for (int color = 0; color < 3; color++) { - switch ((VKRRenderPassAction)color) { - case VKRRenderPassAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break; - case VKRRenderPassAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break; - case VKRRenderPassAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break; - } - int index = RPIndex((VKRRenderPassAction)color, (VKRRenderPassAction)depth); - vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[index]); - } - } -} - void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); // If this is the first drawing command, merge it into the pass. @@ -605,7 +463,6 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; - curFramebuffer_ = VK_NULL_HANDLE; } void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter) { @@ -631,7 +488,6 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, std::unique_lock lock(mutex_); steps_.push_back(step); curRenderStep_ = nullptr; - curFramebuffer_ = VK_NULL_HANDLE; } VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment) { @@ -655,7 +511,6 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Flush() { curRenderStep_ = nullptr; - curFramebuffer_ = VK_NULL_HANDLE; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (frameData.hasInitCommands) { @@ -676,7 +531,7 @@ void VulkanRenderManager::Flush() { void VulkanRenderManager::Run(int frame) { FrameData &frameData = frameData_[frame]; - auto &stepsOnThread_ = frameData_[frame].steps; + auto &stepsOnThread = frameData_[frame].steps; VkDevice device = vulkan_->GetDevice(); uint32_t curSwapchainImage = 0; @@ -687,10 +542,10 @@ void VulkanRenderManager::Run(int frame) { VkCommandBuffer cmd = frameData.mainCmd; - VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin.pInheritanceInfo = nullptr; res = vkBeginCommandBuffer(cmd, &begin); + assert(res == VK_SUCCESS); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR @@ -699,26 +554,10 @@ void VulkanRenderManager::Run(int frame) { assert(res == VK_SUCCESS); TransitionFromPresent(cmd, swapchainImages_[curSwapchainImage].image); - // Optimizes renderpasses, then sequences them. - for (int i = 0; i < stepsOnThread_.size(); i++) { - const VKRStep &step = *stepsOnThread_[i]; - switch (step.stepType) { - case VKRStepType::RENDER: - PerformRenderPass(step, cmd, curSwapchainImage); - break; - case VKRStepType::COPY: - PerformCopy(step, cmd); - break; - case VKRStepType::BLIT: - PerformBlit(step, cmd); - break; - case VKRStepType::READBACK: - // PerformReadback - break; - } - delete stepsOnThread_[i]; - } - stepsOnThread_.clear(); + queueRunner_.SetBackbuffer(framebuffers_[curSwapchainImage]); + queueRunner_.RunSteps(cmd, stepsOnThread); + stepsOnThread.clear(); + insideFrame_ = false; TransitionToPresent(frameData.mainCmd, swapchainImages_[curSwapchainImage].image); @@ -742,6 +581,7 @@ void VulkanRenderManager::Run(int frame) { VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &acquireSemaphore_; + VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; submit_info.pWaitDstStageMask = waitStage; submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); @@ -777,552 +617,3 @@ void VulkanRenderManager::Run(int frame) { VLOG("PULL: Finished running frame %d", frame); } - -void VulkanRenderManager::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd, int swapChainImage) { - // TODO: If there are multiple, we can transition them together. - for (const auto &iter : step.preTransitions) { - if (iter.fb->color.layout != iter.targetLayout) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = iter.fb->color.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = iter.fb->color.image; - barrier.srcAccessMask = 0; - VkPipelineStageFlags srcStage; - VkPipelineStageFlags dstStage; - switch (barrier.oldLayout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - default: - Crash(); - break; - } - barrier.newLayout = iter.targetLayout; - switch (barrier.newLayout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - default: - Crash(); - break; - } - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - - vkCmdPipelineBarrier(cmd, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); - iter.fb->color.layout = barrier.newLayout; - } - } - - // This is supposed to bind a vulkan render pass to the command buffer. - PerformBindFramebufferAsRenderTarget(step, cmd, swapChainImage); - - VKRFramebuffer *fb = step.render.framebuffer; - - VkPipeline lastPipeline = VK_NULL_HANDLE; - - auto &commands = step.commands; - - // TODO: Dynamic state commands (SetViewport, SetScissor, SetBlendConstants, SetStencil*) are only - // valid when a pipeline is bound with those as dynamic state. So we need to add some state tracking here - // for this to be correct. This is a bit of a pain but also will let us eliminate redundant calls. - - for (const auto &c : commands) { - switch (c.cmd) { - case VKRRenderCommand::BIND_PIPELINE: - if (c.pipeline.pipeline != lastPipeline) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.pipeline.pipeline); - lastPipeline = c.pipeline.pipeline; - } - break; - - case VKRRenderCommand::VIEWPORT: - vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp); - break; - - case VKRRenderCommand::SCISSOR: - vkCmdSetScissor(cmd, 0, 1, &c.scissor.scissor); - break; - - case VKRRenderCommand::BLEND: - vkCmdSetBlendConstants(cmd, c.blendColor.color); - break; - - case VKRRenderCommand::STENCIL: - vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); - vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); - vkCmdSetStencilReference(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilRef); - break; - - case VKRRenderCommand::DRAW_INDEXED: - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); - vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); - vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset); - vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); - break; - - case VKRRenderCommand::DRAW: - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); - vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); - vkCmdDraw(cmd, c.draw.count, 1, 0, 0); - break; - - case VKRRenderCommand::CLEAR: - { - int numAttachments = 0; - VkClearRect rc{}; - rc.baseArrayLayer = 0; - rc.layerCount = 1; - rc.rect.extent.width = curWidth_; - rc.rect.extent.height = curHeight_; - VkClearAttachment attachments[2]; - if (c.clear.clearMask & VK_IMAGE_ASPECT_COLOR_BIT) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - attachment.colorAttachment = 0; - Uint8x4ToFloat4(attachment.clearValue.color.float32, c.clear.clearColor); - } - if (c.clear.clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - VkClearAttachment &attachment = attachments[numAttachments++]; - attachment.aspectMask = 0; - if (c.clear.clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) { - attachment.clearValue.depthStencil.depth = c.clear.clearZ; - attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (c.clear.clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) { - attachment.clearValue.depthStencil.stencil = c.clear.clearStencil; - attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } - if (numAttachments) { - vkCmdClearAttachments(cmd, numAttachments, attachments, 1, &rc); - } - break; - } - default: - ELOG("Unimpl queue command"); - ; - } - } - vkCmdEndRenderPass(cmd); - - // Transition the framebuffer if requested. - if (fb && step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = fb->color.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = fb->color.image; - barrier.srcAccessMask = 0; - switch (barrier.oldLayout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - break; - default: - Crash(); - } - barrier.newLayout = step.render.finalColorLayout; - switch (barrier.newLayout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - break; - default: - Crash(); - } - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - - // we're between passes so it's OK. - // ARM Best Practices guide recommends these stage bits. - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - fb->color.layout = barrier.newLayout; - } -} - -void VulkanRenderManager::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd, int swapChainImage) { - VkFramebuffer framebuf; - int w; - int h; - VkImageLayout prevLayout; - if (step.render.framebuffer) { - VKRFramebuffer *fb = step.render.framebuffer; - framebuf = fb->framebuf; - w = fb->width; - h = fb->height; - prevLayout = fb->color.layout; - } else { - framebuf = framebuffers_[swapChainImage]; - w = vulkan_->GetBackbufferWidth(); - h = vulkan_->GetBackbufferHeight(); - } - -#if 0 - // This part is based on faulty old thinking. - if (framebuf == curFramebuffer_) { - if (framebuf == 0) - Crash(); - - // If we're asking to clear, but already bound, we'll just keep it bound but send a clear command. - // We will try to avoid this as much as possible. - VkClearAttachment clear[2]{}; - int count = 0; - if (step.render.color == VKRRenderPassAction::CLEAR) { - clear[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - Uint8x4ToFloat4(clear[count].clearValue.color.float32, step.render.clearColor); - clear[count].colorAttachment = 0; - count++; - } - - if (step.render.depthStencil == VKRRenderPassAction::CLEAR) { - clear[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - clear[count].clearValue.depthStencil.depth = step.render.clearDepth; - clear[count].clearValue.depthStencil.stencil = step.render.clearStencil; - clear[count].colorAttachment = 0; - count++; - } - - if (count > 0) { - VkClearRect rc{ { 0,0,(uint32_t)w,(uint32_t)h }, 0, 1 }; - vkCmdClearAttachments(cmd, count, clear, 1, &rc); - } - // We're done. - return; - } -#endif - - VkRenderPass renderPass; - int numClearVals = 0; - VkClearValue clearVal[2]; - memset(clearVal, 0, sizeof(clearVal)); - if (step.render.framebuffer) { - VKRFramebuffer *fb = step.render.framebuffer; - // Now, if the image needs transitioning, let's transition. - // The backbuffer does not, that's handled by VulkanContext. - if (step.render.framebuffer->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - VkAccessFlags srcAccessMask; - VkPipelineStageFlags srcStage; - switch (fb->color.layout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - default: - Crash(); - break; - } - - TransitionImageLayout2(cmd, fb->color.image, VK_IMAGE_ASPECT_COLOR_BIT, - fb->color.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT); - fb->color.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } - if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - VkAccessFlags srcAccessMask; - VkPipelineStageFlags srcStage; - switch (fb->depth.layout) { - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - default: - Crash(); - break; - } - TransitionImageLayout2(cmd, fb->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, - fb->depth.layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - srcStage, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - srcAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT); - fb->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } - - renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; - // VLOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); - if (step.render.color == VKRRenderPassAction::CLEAR) { - Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); - numClearVals = 1; - } - if (step.render.depthStencil == VKRRenderPassAction::CLEAR) { - clearVal[1].depthStencil.depth = step.render.clearDepth; - clearVal[1].depthStencil.stencil = step.render.clearStencil; - numClearVals = 2; - } - } else { - renderPass = GetBackbufferRenderpass(); - numClearVals = 2; // We don't bother with a depth buffer here. - clearVal[1].depthStencil.depth = 0.0f; - clearVal[1].depthStencil.stencil = 0; - } - - VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - rp_begin.renderPass = renderPass; - rp_begin.framebuffer = framebuf; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = w; - rp_begin.renderArea.extent.height = h; - rp_begin.clearValueCount = numClearVals; - rp_begin.pClearValues = numClearVals ? clearVal : nullptr; - vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); - curWidth_ = w; - curHeight_ = h; - curFramebuffer_ = framebuf; -} - -void VulkanRenderManager::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) { - VKRFramebuffer *src = step.copy.src; - VKRFramebuffer *dst = step.copy.dst; - - VkImageCopy copy{}; - copy.srcOffset.x = step.copy.srcRect.offset.x; - copy.srcOffset.y = step.copy.srcRect.offset.y; - copy.srcOffset.z = 0; - copy.srcSubresource.mipLevel = 0; - copy.srcSubresource.layerCount = 1; - copy.dstOffset.x = step.copy.dstPos.x; - copy.dstOffset.y = step.copy.dstPos.y; - copy.dstOffset.z = 0; - copy.dstSubresource.mipLevel = 0; - copy.dstSubresource.layerCount = 1; - copy.extent.width = step.copy.srcRect.extent.width; - copy.extent.height = step.copy.srcRect.extent.height; - copy.extent.depth = 1; - - VkImageMemoryBarrier srcBarriers[2]{}; - VkImageMemoryBarrier dstBarriers[2]{}; - int srcCount = 0; - int dstCount = 0; - - VkPipelineStageFlags srcStage = 0; - VkPipelineStageFlags dstStage = 0; - // First source barriers. - if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { - if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); - } - if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); - } - } - - // We can't copy only depth or only stencil unfortunately. - if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - } - - if (srcCount) { - vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); - } - if (dstCount) { - vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); - } - - if (step.copy.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { - copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, ©); - } - if (step.copy.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - copy.srcSubresource.aspectMask = 0; - copy.dstSubresource.aspectMask = 0; - if (step.copy.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { - copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (step.copy.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { - copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, ©); - } -} - -void VulkanRenderManager::PerformBlit(const VKRStep &step, VkCommandBuffer cmd) { - VkImageMemoryBarrier srcBarriers[2]{}; - VkImageMemoryBarrier dstBarriers[2]{}; - - VKRFramebuffer *src = step.blit.src; - VKRFramebuffer *dst = step.blit.dst; - - // If any validation needs to be performed here, it should probably have been done - // already when the blit was queued. So don't validate here. - VkImageBlit blit{}; - blit.srcOffsets[0].x = step.blit.srcRect.offset.x; - blit.srcOffsets[0].y = step.blit.srcRect.offset.y; - blit.srcOffsets[0].z = 0; - blit.srcOffsets[1].x = step.blit.srcRect.offset.x + step.blit.srcRect.extent.width; - blit.srcOffsets[1].y = step.blit.srcRect.offset.y + step.blit.srcRect.extent.height; - blit.srcOffsets[1].z = 1; - blit.srcSubresource.mipLevel = 0; - blit.srcSubresource.layerCount = 1; - blit.dstOffsets[0].x = step.blit.dstRect.offset.x; - blit.dstOffsets[0].y = step.blit.dstRect.offset.y; - blit.dstOffsets[0].z = 0; - blit.dstOffsets[1].x = step.blit.dstRect.offset.x + step.blit.dstRect.extent.width; - blit.dstOffsets[1].y = step.blit.dstRect.offset.y + step.blit.dstRect.extent.height; - blit.dstOffsets[1].z = 1; - blit.dstSubresource.mipLevel = 0; - blit.dstSubresource.layerCount = 1; - - VkPipelineStageFlags srcStage = 0; - VkPipelineStageFlags dstStage = 0; - - int srcCount = 0; - int dstCount = 0; - - // First source barriers. - if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { - if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_COLOR_BIT); - } - if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_COLOR_BIT); - } - } - - // We can't copy only depth or only stencil unfortunately. - if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { - SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], srcStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], dstStage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); - } - } - - if (srcCount) { - vkCmdPipelineBarrier(cmd, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers); - } - if (dstCount) { - vkCmdPipelineBarrier(cmd, dstStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers); - } - - if (step.blit.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { - blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, step.blit.filter); - } - - // TODO: Need to check if the depth format is blittable. - // Actually, we should probably almost always use copies rather than blits for depth buffers. - if (step.blit.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { - blit.srcSubresource.aspectMask = 0; - blit.dstSubresource.aspectMask = 0; - if (step.blit.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { - blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (step.blit.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { - blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - vkCmdBlitImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &blit, step.blit.filter); - } -} - -void VulkanRenderManager::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = img.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; - switch (img.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; - stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - default: - Crash(); - } - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - img.layout = barrier.newLayout; -} - -void VulkanRenderManager::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) { - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = img.layout; - barrier.subresourceRange.layerCount = 1; - barrier.subresourceRange.levelCount = 1; - barrier.image = img.image; - barrier.srcAccessMask = 0; - switch (img.layout) { - case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - break; - case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - stage |= VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - break; - case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - default: - Crash(); - } - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - img.layout = barrier.newLayout; -} diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 780481f669..9a5705e083 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -8,140 +8,12 @@ #include "Common/Vulkan/VulkanContext.h" #include "math/dataconv.h" #include "thin3d/thin3d.h" +#include "thin3d/VulkanQueueRunner.h" // Takes the role that a GL driver does of sequencing and optimizing render passes. // Only draws and binds are handled here, resource creation and allocations are handled as normal - // that's the nice thing with Vulkan. -// The cool thing is that you can Flush on a different thread than you record the commands on! - -enum class VKRRenderCommand : uint8_t { - BIND_PIPELINE, - STENCIL, - BLEND, - VIEWPORT, - SCISSOR, - CLEAR, - DRAW, - DRAW_INDEXED, -}; - -struct VkRenderData { - VKRRenderCommand cmd; - union { - struct { - VkPipeline pipeline; - } pipeline; - struct { - VkPipelineLayout pipelineLayout; - VkDescriptorSet ds; - int numUboOffsets; - uint32_t uboOffsets[3]; - VkBuffer vbuffer; - VkDeviceSize voffset; - uint32_t count; - } draw; - struct { - VkPipelineLayout pipelineLayout; - VkDescriptorSet ds; - int numUboOffsets; - uint32_t uboOffsets[3]; - VkBuffer vbuffer; // might need to increase at some point - VkDeviceSize voffset; - VkBuffer ibuffer; - VkDeviceSize ioffset; - uint32_t count; - int16_t instances; - VkIndexType indexType; - } drawIndexed; - struct { - uint32_t clearColor; - float clearZ; - int clearStencil; - int clearMask; // VK_IMAGE_ASPECT_COLOR_BIT etc - } clear; - struct { - VkViewport vp; - } viewport; - struct { - VkRect2D scissor; - } scissor; - struct { - uint8_t stencilWriteMask; - uint8_t stencilCompareMask; - uint8_t stencilRef; - } stencil; - struct { - float color[4]; - } blendColor; - struct { - - } beginRp; - struct { - - } endRp; - }; -}; - -enum class VKRStepType : uint8_t { - RENDER, - COPY, - BLIT, - READBACK, -}; - -class VKRFramebuffer; - -enum class VKRRenderPassAction { - DONT_CARE, - CLEAR, - KEEP, -}; - -struct TransitionRequest { - VKRFramebuffer *fb; - VkImageLayout targetLayout; -}; - -struct VKRStep { - VKRStep(VKRStepType _type) : stepType(_type) {} - VKRStepType stepType; - std::vector commands; - std::vector preTransitions; - union { - struct { - VKRFramebuffer *framebuffer; - VKRRenderPassAction color; - VKRRenderPassAction depthStencil; - uint32_t clearColor; - float clearDepth; - int clearStencil; - int numDraws; - VkImageLayout finalColorLayout; - } render; - struct { - VKRFramebuffer *src; - VKRFramebuffer *dst; - VkRect2D srcRect; - VkOffset2D dstPos; - int aspectMask; - } copy; - struct { - VKRFramebuffer *src; - VKRFramebuffer *dst; - VkRect2D srcRect; - VkRect2D dstRect; - int aspectMask; - VkFilter filter; - } blit; - struct { - VKRFramebuffer *src; - void *destPtr; - VkRect2D srcRect; - } readback; - }; -}; - // Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine // to let them have individual non-pooled allocations. Until it's not fine. We'll see. struct VKRImage { @@ -211,6 +83,7 @@ public: void Run(int frame); // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); + void RunSteps(VkCommandBuffer cmd, const std::vector &steps, int curSwapChainImage); void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -292,17 +165,18 @@ public: } VkCommandBuffer GetInitCmd(); - VkRenderPass GetBackbufferRenderpass() const { - return backbufferRenderPass_; + + VkRenderPass GetRenderPass(int pass) const { + return queueRunner_.GetRenderPass(pass); } - VkRenderPass GetRenderPass(int i) const { - return renderPasses_[i]; + VkRenderPass GetBackbufferRenderPass() const { + return queueRunner_.GetBackbufferRenderPass(); } - VkRenderPass GetCompatibleRenderpass() const { + VkRenderPass GetCompatibleRenderPass() const { if (curRenderStep_ && curRenderStep_->render.framebuffer != nullptr) { - return GetRenderPass(0); + return queueRunner_.GetRenderPass(0); } else { - return backbufferRenderPass_; + return queueRunner_.GetBackbufferRenderPass(); } } @@ -311,30 +185,10 @@ public: private: void InitBackbufferFramebuffers(int width, int height); - void InitBackbufferRenderPass(); - void InitRenderpasses(); void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. - - void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd, int swapChainImage); - - void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd, int swapChainImage); - void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd); - void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); - - inline int RPIndex(VKRRenderPassAction color, VKRRenderPassAction depth) { - return (int)depth * 3 + (int)color; - } - - static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); - static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); - // Permanent objects VkSemaphore acquireSemaphore_; VkSemaphore renderingCompleteSemaphore_; - VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; - // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. - // TODO: Create these on demand. - VkRenderPass renderPasses_[9]; // Per-frame data, round-robin so we can overlap submission with execution of the previous frame. struct FrameData { @@ -370,7 +224,7 @@ private: VulkanContext *vulkan_; std::thread thread_; std::mutex mutex_; - VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; + VulkanQueueRunner queueRunner_; // Swap chain management struct SwapchainImageData { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 005523b991..b0f0d08a9b 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -448,9 +448,9 @@ public: // Return a representative renderpass. return (uintptr_t)renderManager_.GetRenderPass(0); case NativeObject::BACKBUFFER_RENDERPASS: - return (uintptr_t)renderManager_.GetBackbufferRenderpass(); + return (uintptr_t)renderManager_.GetBackbufferRenderPass(); case NativeObject::COMPATIBLE_RENDERPASS: - return (uintptr_t)renderManager_.GetCompatibleRenderpass(); + return (uintptr_t)renderManager_.GetCompatibleRenderPass(); case NativeObject::INIT_COMMANDBUFFER: return (uintptr_t)renderManager_.GetInitCmd(); case NativeObject::BOUND_TEXTURE_IMAGEVIEW: @@ -893,7 +893,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically. info.layout = pipelineLayout_; info.subpass = 0; - info.renderPass = renderManager_.GetBackbufferRenderpass(); + info.renderPass = renderManager_.GetBackbufferRenderPass(); // OK, need to create a new pipeline. VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 1, &info, nullptr, &pipeline->vkpipeline); From 962ada1700d68a4b8860225c06b07792907f6c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 28 Oct 2017 00:37:06 +0200 Subject: [PATCH 080/111] Comment cleanup --- ext/native/thin3d/VulkanQueueRunner.h | 2 -- ext/native/thin3d/VulkanRenderManager.h | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index e72c986325..0b2a38064d 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -9,8 +9,6 @@ class VKRFramebuffer; struct VKRImage; -// The cool thing is that you can Flush on a different thread than you record the commands on! - enum class VKRRenderCommand : uint8_t { BIND_PIPELINE, STENCIL, diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 9a5705e083..f0c1a06c04 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -1,5 +1,9 @@ #pragma once +// VulkanRenderManager takes the role that a GL driver does of sequencing and optimizing render passes. +// Only draws and binds are handled here, resource creation and allocations are handled as normal - +// that's the nice thing with Vulkan. + #include #include #include @@ -10,10 +14,6 @@ #include "thin3d/thin3d.h" #include "thin3d/VulkanQueueRunner.h" -// Takes the role that a GL driver does of sequencing and optimizing render passes. -// Only draws and binds are handled here, resource creation and allocations are handled as normal - -// that's the nice thing with Vulkan. - // Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine // to let them have individual non-pooled allocations. Until it's not fine. We'll see. struct VKRImage { From 717ec2387b39293063324cfa7b025819af19104f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 28 Oct 2017 16:47:08 +0200 Subject: [PATCH 081/111] VulkanRenderManager: Split out BeginFrame and EndFrame from Run --- ext/native/thin3d/VulkanRenderManager.cpp | 71 +++++++++++++---------- ext/native/thin3d/VulkanRenderManager.h | 12 +++- ext/native/thin3d/thin3d_vulkan.cpp | 2 +- 3 files changed, 52 insertions(+), 33 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 77b9f5f473..6c9c18ffc1 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -509,7 +509,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in return fb->color.imageView; } -void VulkanRenderManager::Flush() { +void VulkanRenderManager::Finish() { curRenderStep_ = nullptr; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; @@ -529,46 +529,42 @@ void VulkanRenderManager::Flush() { vulkan_->EndFrame(); } -void VulkanRenderManager::Run(int frame) { +// Can be called multiple times with no bad side effects. This is so that we can either begin a frame the normal way, +void VulkanRenderManager::BeginFrame(int frame) { FrameData &frameData = frameData_[frame]; - auto &stepsOnThread = frameData_[frame].steps; - VkDevice device = vulkan_->GetDevice(); + if (!frameData.hasBegun) { + // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. + // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. + VkResult res = vkAcquireNextImageKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &frameData.curSwapchainImage); + assert(res == VK_SUCCESS); + // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR + // return codes - uint32_t curSwapchainImage = 0; - // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. - // Now, I wonder if we should do this early in the frame or late? Right now we do it early, which should be fine. - VkResult res = vkAcquireNextImageKHR(device, vulkan_->GetSwapchain(), UINT64_MAX, acquireSemaphore_, (VkFence)VK_NULL_HANDLE, &curSwapchainImage); - assert(res == VK_SUCCESS); + VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + res = vkBeginCommandBuffer(frameData.mainCmd, &begin); - VkCommandBuffer cmd = frameData.mainCmd; + assert(res == VK_SUCCESS); - VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - res = vkBeginCommandBuffer(cmd, &begin); + // TODO: Is it best to do this here, or combine with some other transition, or just do it right before the backbuffer bind-for-render? + TransitionFromPresent(frameData.mainCmd, swapchainImages_[frameData.curSwapchainImage].image); - assert(res == VK_SUCCESS); + queueRunner_.SetBackbuffer(framebuffers_[frameData.curSwapchainImage]); - // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR - // return codes - // TODO: Is it best to do this here, or combine with some other transition, or just do it right before the backbuffer bind-for-render? - assert(res == VK_SUCCESS); - TransitionFromPresent(cmd, swapchainImages_[curSwapchainImage].image); - - queueRunner_.SetBackbuffer(framebuffers_[curSwapchainImage]); - queueRunner_.RunSteps(cmd, stepsOnThread); - stepsOnThread.clear(); + frameData.hasBegun = true; + } +} +void VulkanRenderManager::EndFrame(int frame) { + FrameData &frameData = frameData_[frame]; + frameData.hasBegun = false; insideFrame_ = false; - TransitionToPresent(frameData.mainCmd, swapchainImages_[curSwapchainImage].image); + TransitionToPresent(frameData.mainCmd, swapchainImages_[frameData.curSwapchainImage].image); - res = vkEndCommandBuffer(frameData.mainCmd); + VkResult res = vkEndCommandBuffer(frameData.mainCmd); assert(res == VK_SUCCESS); - // So the sequence will be, cmdInit, [cmdQueue_], frame->cmdBuf. - // This way we bunch up all the initialization needed for the frame, we render to - // other buffers before the back buffer, and then last we render to the backbuffer. - int numCmdBufs = 0; std::vector cmdBufs; if (frameData.hasInitCommands) { @@ -602,7 +598,7 @@ void VulkanRenderManager::Run(int frame) { VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; present.swapchainCount = 1; present.pSwapchains = &swapchain; - present.pImageIndices = &curSwapchainImage; + present.pImageIndices = &frameData.curSwapchainImage; present.pWaitSemaphores = &renderingCompleteSemaphore_; present.waitSemaphoreCount = 1; present.pResults = nullptr; @@ -614,6 +610,21 @@ void VulkanRenderManager::Run(int frame) { } else { assert(res == VK_SUCCESS); } +} + +void VulkanRenderManager::Run(int frame) { + VkDevice device = vulkan_->GetDevice(); + + BeginFrame(frame); + + FrameData &frameData = frameData_[frame]; + auto &stepsOnThread = frameData_[frame].steps; + VkCommandBuffer cmd = frameData.mainCmd; + queueRunner_.RunSteps(cmd, stepsOnThread); + stepsOnThread.clear(); + + EndFrame(frame); + VLOG("PULL: Finished running frame %d", frame); } diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index f0c1a06c04..85c04264db 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -78,12 +78,13 @@ public: // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. void BeginFrame(); + void EndFrame(); // Can run on a different thread! Just make sure to use BeginFrameWrites. - void Flush(); + void Finish(); void Run(int frame); + // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void Sync(); - void RunSteps(VkCommandBuffer cmd, const std::vector &steps, int curSwapChainImage); void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -186,6 +187,9 @@ public: private: void InitBackbufferFramebuffers(int width, int height); void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. + void BeginFrame(int frame); + void EndFrame(int frame); + // Permanent objects VkSemaphore acquireSemaphore_; VkSemaphore renderingCompleteSemaphore_; @@ -209,6 +213,10 @@ private: VkCommandBuffer mainCmd; bool hasInitCommands = false; std::vector steps; + + // Swapchain. + bool hasBegun = false; + uint32_t curSwapchainImage; }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b0f0d08a9b..7aba729f65 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -757,7 +757,7 @@ void VKContext::EndFrame() { // Stop collecting data in the frame's data pushbuffer. push_->End(); - renderManager_.Flush(); + renderManager_.Finish(); frameNum_++; if (frameNum_ >= vulkan_->GetInflightFrames()) From b98d4e5c9d8cec48f96189edf364ad3d71c127ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 28 Oct 2017 18:03:27 +0200 Subject: [PATCH 082/111] Vulkan: Rough untested implementation of synchronous readbacks. --- Windows/GEDebugger/GEDebugger.cpp | 3 +- Windows/GEDebugger/GEDebugger.h | 36 ++++----- ext/native/thin3d/VulkanQueueRunner.cpp | 66 ++++++++++++++++- ext/native/thin3d/VulkanQueueRunner.h | 14 +++- ext/native/thin3d/VulkanRenderManager.cpp | 89 ++++++++++++++++++----- ext/native/thin3d/VulkanRenderManager.h | 15 ++-- ext/native/thin3d/thin3d_vulkan.cpp | 14 ++++ 7 files changed, 187 insertions(+), 50 deletions(-) diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 3e0a2cd5af..cb68ae915a 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -61,8 +61,7 @@ void CGEDebugger::Init() { } CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent) - : Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent), primaryWindow(nullptr), secondWindow(nullptr), - textureLevel_(0), showClut_(false), primaryBuffer_(nullptr), secondBuffer_(nullptr) { + : Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent) { GPUBreakpoints::Init(); Core_ListenShutdown(ForceUnpause); diff --git a/Windows/GEDebugger/GEDebugger.h b/Windows/GEDebugger/GEDebugger.h index 10ca029945..f4f6aa3db5 100644 --- a/Windows/GEDebugger/GEDebugger.h +++ b/Windows/GEDebugger/GEDebugger.h @@ -85,25 +85,25 @@ private: u32 TexturePreviewFlags(const GPUgstate &state); - CtrlDisplayListView *displayList; - TabDisplayLists *lists; - TabStateFlags *flags; - TabStateLighting *lighting; - TabStateTexture *textureState; - TabStateSettings *settings; - TabVertices *vertices; - TabMatrices *matrices; - SimpleGLWindow *primaryWindow; - SimpleGLWindow *secondWindow; - TabStateWatch *watch; - TabControl *tabs; - TabControl *fbTabs; - int textureLevel_; - bool showClut_; - bool forceOpaque_; + CtrlDisplayListView *displayList = nullptr; + TabDisplayLists *lists = nullptr; + TabStateFlags *flags = nullptr; + TabStateLighting *lighting = nullptr; + TabStateTexture *textureState = nullptr; + TabStateSettings *settings = nullptr; + TabVertices *vertices = nullptr; + TabMatrices *matrices = nullptr; + SimpleGLWindow *primaryWindow = nullptr; + SimpleGLWindow *secondWindow = nullptr; + TabStateWatch *watch = nullptr; + TabControl *tabs = nullptr; + TabControl *fbTabs = nullptr; + int textureLevel_ = 0; + bool showClut_ = false; + bool forceOpaque_ = false; // The most recent primary/framebuffer and texture buffers. - const GPUDebugBuffer *primaryBuffer_; - const GPUDebugBuffer *secondBuffer_; + const GPUDebugBuffer *primaryBuffer_ = nullptr; + const GPUDebugBuffer *secondBuffer_ = nullptr; int minWidth_; int minHeight_; diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index c101c563bf..ff70dc1d4e 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -1,13 +1,40 @@ #include "VulkanQueueRunner.h" #include "VulkanRenderManager.h" +const uint32_t readbackBufferSize = 2048 * 2048 * 4; + void VulkanQueueRunner::CreateDeviceObjects() { InitBackbufferRenderPass(); InitRenderpasses(); + + VkDevice device = vulkan_->GetDevice(); + + VkBufferCreateInfo buf{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + buf.size = readbackBufferSize; + buf.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + vkCreateBuffer(device, &buf, nullptr, &readbackBuffer_); + + VkMemoryRequirements reqs{}; + vkGetBufferMemoryRequirements(device, readbackBuffer_, &reqs); + + VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + alloc.allocationSize = reqs.size; + + VkFlags typeReqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + bool success = vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &alloc.memoryTypeIndex); + assert(success); + vkAllocateMemory(device, &alloc, nullptr, &readbackMemory_); + + uint32_t offset = 0; + vkBindBufferMemory(device, readbackBuffer_, readbackMemory_, offset); } void VulkanQueueRunner::DestroyDeviceObjects() { VkDevice device = vulkan_->GetDevice(); + vkFreeMemory(device, readbackMemory_, nullptr); + vulkan_->Delete().QueueDeleteBuffer(readbackBuffer_); + for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) { assert(renderPasses_[i] != VK_NULL_HANDLE); vkDestroyRenderPass(device, renderPasses_[i], nullptr); @@ -16,7 +43,6 @@ void VulkanQueueRunner::DestroyDeviceObjects() { vkDestroyRenderPass(device, backbufferRenderPass_, nullptr); } - void VulkanQueueRunner::InitBackbufferRenderPass() { VkResult U_ASSERT_ONLY res; @@ -167,7 +193,7 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vectorcolor; + } else if (step.readback.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + srcImage = &step.readback.src->depth; + } else { + assert(false); + } + + VkBufferImageCopy region{}; + region.imageOffset = { step.readback.srcRect.offset.x, step.readback.srcRect.offset.y, 0 }; + region.imageExtent = { step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, 1 }; + region.imageSubresource.aspectMask = step.readback.aspectMask; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = step.readback.srcRect.extent.height; + vkCmdCopyImageToBuffer(cmd, srcImage->image, srcImage->layout, readbackBuffer_, 1, ®ion); + + // NOTE: Can't read the buffer using the CPU here - need to sync first. +} + +void VulkanQueueRunner::CopyReadbackBuffer(const VKRStep &step) { + // Read back to the requested address in ram from buffer. + void *mappedData; + VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, step.readback.srcRect.extent.width * step.readback.srcRect.extent.height * 4, 0, &mappedData); + assert(res == VK_SUCCESS); + + const int pixelSize = 4; // TODO: Fix. + for (int y = 0; y < step.readback.srcRect.extent.height; y++) { + const uint8_t *src = (const uint8_t *)mappedData + step.readback.srcRect.extent.width * y; + uint8_t *dst = (uint8_t *)step.readback.destPtr + step.readback.pixelStride * pixelSize * y; + } + vkUnmapMemory(vulkan_->GetDevice(), readbackMemory_); +} diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 0b2a38064d..c8d84fd151 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -127,8 +127,10 @@ struct VKRStep { VkFilter filter; } blit; struct { + int aspectMask; VKRFramebuffer *src; - void *destPtr; + uint8_t *destPtr; + int pixelStride; VkRect2D srcRect; } readback; }; @@ -156,6 +158,8 @@ public: return (int)depth * 3 + (int)color; } + void CopyReadbackBuffer(const VKRStep &step); + private: void InitBackbufferRenderPass(); void InitRenderpasses(); @@ -164,6 +168,7 @@ private: void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd); void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd); void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); + void PerformReadback(const VKRStep &pass, VkCommandBuffer cmd); static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); @@ -177,4 +182,9 @@ private: // Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents. // TODO: Create these on demand. VkRenderPass renderPasses_[9]{}; -}; \ No newline at end of file + + // Readback buffer. Currently we only support synchronous readback, so we only really need one. + // We size it generously. + VkDeviceMemory readbackMemory_; + VkBuffer readbackBuffer_; +}; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 6c9c18ffc1..17cf3afd3f 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -287,9 +287,11 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (!frameData.hasInitCommands) { - VkCommandBufferBeginInfo begin = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin.pInheritanceInfo = nullptr; + VkCommandBufferBeginInfo begin = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + nullptr, + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + }; VkResult res = vkBeginCommandBuffer(frameData.initCmd, &begin); assert(res == VK_SUCCESS); frameData.hasInitCommands = true; @@ -297,10 +299,6 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { return frameData_[curFrame].initCmd; } -void VulkanRenderManager::Sync() { - -} - void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { // Eliminate dupes. if (steps_.size() && steps_.back()->stepType == VKRStepType::RENDER && steps_.back()->render.framebuffer == fb) { @@ -327,6 +325,25 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); } +void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, uint8_t *pixels, int pixelStride) { + VKRStep *step = new VKRStep{ VKRStepType::READBACK }; + step->readback.aspectMask = aspectBits; + step->readback.destPtr = (uint8_t *)pixels; + step->readback.pixelStride = pixelStride; + step->readback.src = src; + step->readback.srcRect.offset = { x, y }; + step->readback.srcRect.extent = { (uint32_t)w, (uint32_t)h }; + steps_.push_back(step); + + curRenderStep_ = nullptr; + + FlushSync(); + + // Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM. + queueRunner_.CopyReadbackBuffer(*step); +} + + void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { VkResult U_ASSERT_ONLY res; // We share the same depth buffer but have multiple color buffers, see the loop below. @@ -513,9 +530,6 @@ void VulkanRenderManager::Finish() { curRenderStep_ = nullptr; int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; - if (frameData.hasInitCommands) { - vkEndCommandBuffer(frameData.initCmd); - } if (!useThread) { frameData.steps = std::move(steps_); Run(curFrame); @@ -530,7 +544,7 @@ void VulkanRenderManager::Finish() { } // Can be called multiple times with no bad side effects. This is so that we can either begin a frame the normal way, -void VulkanRenderManager::BeginFrame(int frame) { +void VulkanRenderManager::BeginSubmitFrame(int frame) { FrameData &frameData = frameData_[frame]; if (!frameData.hasBegun) { // Get the index of the next available swapchain image, and a semaphore to block command buffer execution on. @@ -555,18 +569,19 @@ void VulkanRenderManager::BeginFrame(int frame) { } } -void VulkanRenderManager::EndFrame(int frame) { +void VulkanRenderManager::Submit(int frame) { FrameData &frameData = frameData_[frame]; - frameData.hasBegun = false; - insideFrame_ = false; - - TransitionToPresent(frameData.mainCmd, swapchainImages_[frameData.curSwapchainImage].image); + if (frameData.hasInitCommands) { + VkResult res = vkEndCommandBuffer(frameData.initCmd); + assert(res == VK_SUCCESS); + } VkResult res = vkEndCommandBuffer(frameData.mainCmd); assert(res == VK_SUCCESS); int numCmdBufs = 0; std::vector cmdBufs; + cmdBufs.reserve(2); if (frameData.hasInitCommands) { cmdBufs.push_back(frameData.initCmd); frameData.hasInitCommands = false; @@ -593,6 +608,16 @@ void VulkanRenderManager::EndFrame(int frame) { frameData.readyForFence = true; frameData.push_condVar.notify_all(); } +} + +void VulkanRenderManager::EndSubmitFrame(int frame) { + FrameData &frameData = frameData_[frame]; + frameData.hasBegun = false; + insideFrame_ = false; + + TransitionToPresent(frameData.mainCmd, swapchainImages_[frameData.curSwapchainImage].image); + + Submit(frame); VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; @@ -602,7 +627,8 @@ void VulkanRenderManager::EndFrame(int frame) { present.pWaitSemaphores = &renderingCompleteSemaphore_; present.waitSemaphoreCount = 1; present.pResults = nullptr; - res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); + + VkResult res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI // return codes if (res == VK_ERROR_OUT_OF_DATE_KHR) { @@ -615,7 +641,7 @@ void VulkanRenderManager::EndFrame(int frame) { void VulkanRenderManager::Run(int frame) { VkDevice device = vulkan_->GetDevice(); - BeginFrame(frame); + BeginSubmitFrame(frame); FrameData &frameData = frameData_[frame]; auto &stepsOnThread = frameData_[frame].steps; @@ -623,8 +649,33 @@ void VulkanRenderManager::Run(int frame) { queueRunner_.RunSteps(cmd, stepsOnThread); stepsOnThread.clear(); - EndFrame(frame); + EndSubmitFrame(frame); VLOG("PULL: Finished running frame %d", frame); } + +void VulkanRenderManager::FlushSync() { + int frame = vulkan_->GetCurFrame(); + BeginSubmitFrame(frame); + + FrameData &frameData = frameData_[frame]; + auto &stepsOnThread = frameData_[frame].steps; + VkCommandBuffer cmd = frameData.mainCmd; + queueRunner_.RunSteps(cmd, stepsOnThread); + stepsOnThread.clear(); + + Submit(frame); + + vkDeviceWaitIdle(vulkan_->GetDevice()); + + // At this point we can resume filling the command buffers for the current frame since + // we know the device is idle - and thus all previously enqueued command buffers have been processed. + // No need to switch to the next frame number. + VkCommandBufferBeginInfo begin = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + nullptr, + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + }; + vkBeginCommandBuffer(frameData_->mainCmd, &begin); +} diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 85c04264db..ff7849941b 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -78,16 +78,13 @@ public: // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. void BeginFrame(); - void EndFrame(); - // Can run on a different thread! Just make sure to use BeginFrameWrites. + // Can run on a different thread! void Finish(); void Run(int frame); - // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). - void Sync(); - void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); + void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, uint8_t *pixels, int pixelStride); void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask); void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter); @@ -187,8 +184,12 @@ public: private: void InitBackbufferFramebuffers(int width, int height); void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. - void BeginFrame(int frame); - void EndFrame(int frame); + void BeginSubmitFrame(int frame); + void EndSubmitFrame(int frame); + void Submit(int frame); + + // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). + void FlushSync(); // Permanent objects VkSemaphore acquireSemaphore_; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 7aba729f65..16d2c953c7 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -372,6 +372,7 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; + bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride); // These functions should be self explanatory. void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override; @@ -1284,6 +1285,19 @@ bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int sr return true; } +bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) { + VKFramebuffer *src = (VKFramebuffer *)srcfb; + + int aspectMask = 0; + if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + + renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, (uint8_t *)pixels, pixelStride); + return true; +} + + void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { VKFramebuffer *fb = (VKFramebuffer *)fbo; VKRRenderPassAction color = (VKRRenderPassAction)rp.color; // same values. From 34b65c0ca907fef92f315ff39114237c94918423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 28 Oct 2017 18:41:54 +0200 Subject: [PATCH 083/111] Fix Vulkan framebuffer readbacks, as long as they're in R8G8B8A8. --- ext/native/thin3d/VulkanQueueRunner.cpp | 23 +++++++++----- ext/native/thin3d/VulkanQueueRunner.h | 4 +-- ext/native/thin3d/VulkanRenderManager.cpp | 37 ++++++++++++++--------- ext/native/thin3d/VulkanRenderManager.h | 3 +- ext/native/thin3d/thin3d_vulkan.cpp | 7 ++++- 5 files changed, 47 insertions(+), 27 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index ff70dc1d4e..ae4f668199 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -725,28 +725,37 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd assert(false); } + assert(srcImage->format == VK_FORMAT_R8G8B8A8_UNORM); + + VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + VkPipelineStageFlags stage = 0; + SetupTransitionToTransferSrc(*srcImage, barrier, stage, step.readback.aspectMask); + vkCmdPipelineBarrier(cmd, stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + VkBufferImageCopy region{}; region.imageOffset = { step.readback.srcRect.offset.x, step.readback.srcRect.offset.y, 0 }; region.imageExtent = { step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, 1 }; region.imageSubresource.aspectMask = step.readback.aspectMask; + region.imageSubresource.layerCount = 1; region.bufferOffset = 0; - region.bufferRowLength = 0; + region.bufferRowLength = step.readback.srcRect.extent.width; region.bufferImageHeight = step.readback.srcRect.extent.height; vkCmdCopyImageToBuffer(cmd, srcImage->image, srcImage->layout, readbackBuffer_, 1, ®ion); // NOTE: Can't read the buffer using the CPU here - need to sync first. } -void VulkanQueueRunner::CopyReadbackBuffer(const VKRStep &step) { +void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, int pixelStride, uint8_t *pixels) { // Read back to the requested address in ram from buffer. void *mappedData; - VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, step.readback.srcRect.extent.width * step.readback.srcRect.extent.height * 4, 0, &mappedData); + const int pixelSize = 4; // TODO: Fix. + VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, width * height * pixelSize, 0, &mappedData); assert(res == VK_SUCCESS); - const int pixelSize = 4; // TODO: Fix. - for (int y = 0; y < step.readback.srcRect.extent.height; y++) { - const uint8_t *src = (const uint8_t *)mappedData + step.readback.srcRect.extent.width * y; - uint8_t *dst = (uint8_t *)step.readback.destPtr + step.readback.pixelStride * pixelSize * y; + for (int y = 0; y < height; y++) { + const uint8_t *src = (const uint8_t *)mappedData + width * y; + uint8_t *dst = pixels + pixelStride * pixelSize * y; + memcpy(dst, src, width * pixelSize); } vkUnmapMemory(vulkan_->GetDevice(), readbackMemory_); } diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index c8d84fd151..1db5e1abea 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -129,8 +129,6 @@ struct VKRStep { struct { int aspectMask; VKRFramebuffer *src; - uint8_t *destPtr; - int pixelStride; VkRect2D srcRect; } readback; }; @@ -158,7 +156,7 @@ public: return (int)depth * 3 + (int)color; } - void CopyReadbackBuffer(const VKRStep &step); + void CopyReadbackBuffer(int width, int height, int pixelStride, uint8_t *pixels); private: void InitBackbufferRenderPass(); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 17cf3afd3f..9728bcb8e6 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -5,7 +5,7 @@ #include "thin3d/VulkanRenderManager.h" #include "thread/threadutil.h" -#ifdef _DEBUG +#if 0 // def _DEBUG #define VLOG ILOG #else #define VLOG(...) @@ -92,6 +92,8 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, dstStage, 0, dstAccessMask); img.layout = initialLayout; + + img.format = format; } VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan), queueRunner_(vulkan) { @@ -328,8 +330,6 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, uint8_t *pixels, int pixelStride) { VKRStep *step = new VKRStep{ VKRStepType::READBACK }; step->readback.aspectMask = aspectBits; - step->readback.destPtr = (uint8_t *)pixels; - step->readback.pixelStride = pixelStride; step->readback.src = src; step->readback.srcRect.offset = { x, y }; step->readback.srcRect.extent = { (uint32_t)w, (uint32_t)h }; @@ -339,8 +339,8 @@ void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a FlushSync(); - // Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM. - queueRunner_.CopyReadbackBuffer(*step); + // Need to call this after FlushSyfnc so the pixels are guaranteed to be ready in CPU-accessible VRAM. + queueRunner_.CopyReadbackBuffer(w, h, pixelStride, pixels); } @@ -569,7 +569,7 @@ void VulkanRenderManager::BeginSubmitFrame(int frame) { } } -void VulkanRenderManager::Submit(int frame) { +void VulkanRenderManager::Submit(int frame, bool triggerFence) { FrameData &frameData = frameData_[frame]; if (frameData.hasInitCommands) { VkResult res = vkEndCommandBuffer(frameData.initCmd); @@ -590,16 +590,20 @@ void VulkanRenderManager::Submit(int frame) { cmdBufs.push_back(frameData.mainCmd); VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &acquireSemaphore_; + if (triggerFence) { + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &acquireSemaphore_; + } VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; submit_info.pWaitDstStageMask = waitStage; submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); submit_info.pCommandBuffers = cmdBufs.data(); - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; - res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, frameData.fence); + if (triggerFence) { + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; + } + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, triggerFence ? frameData.fence : VK_NULL_HANDLE); assert(res == VK_SUCCESS); if (useThread) { @@ -617,7 +621,7 @@ void VulkanRenderManager::EndSubmitFrame(int frame) { TransitionToPresent(frameData.mainCmd, swapchainImages_[frameData.curSwapchainImage].image); - Submit(frame); + Submit(frame, true); VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; @@ -660,22 +664,25 @@ void VulkanRenderManager::FlushSync() { BeginSubmitFrame(frame); FrameData &frameData = frameData_[frame]; + frameData.steps = std::move(steps_); + auto &stepsOnThread = frameData_[frame].steps; VkCommandBuffer cmd = frameData.mainCmd; queueRunner_.RunSteps(cmd, stepsOnThread); stepsOnThread.clear(); - Submit(frame); + Submit(frame, false); vkDeviceWaitIdle(vulkan_->GetDevice()); // At this point we can resume filling the command buffers for the current frame since // we know the device is idle - and thus all previously enqueued command buffers have been processed. // No need to switch to the next frame number. - VkCommandBufferBeginInfo begin = { + VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT }; - vkBeginCommandBuffer(frameData_->mainCmd, &begin); + VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); + assert(res == VK_SUCCESS); } diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index ff7849941b..3a795f4b0c 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -21,6 +21,7 @@ struct VKRImage { VkImageView imageView; VkDeviceMemory memory; VkImageLayout layout; + VkFormat format; }; void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color); @@ -186,7 +187,7 @@ private: void InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering. void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); - void Submit(int frame); + void Submit(int frame, bool triggerFence); // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void FlushSync(); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 16d2c953c7..e09503e043 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1288,8 +1288,13 @@ bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int sr bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) { VKFramebuffer *src = (VKFramebuffer *)srcfb; + VkFormat vkFormat = DataFormatToVulkan(format); + int aspectMask = 0; - if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + if (channelBits & FBChannel::FB_COLOR_BIT) { + assert(vkFormat == src->GetFB()->color.format); + aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + } if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; From a9f01c45e07d730adfadf6636da1c5851135c5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 28 Oct 2017 19:20:46 +0200 Subject: [PATCH 084/111] Use the CPU color conversion from GLES for readbacks in Vulkan (and D3D11). Fix bug. --- GPU/Common/FramebufferCommon.cpp | 99 ++++++++++++++++++++--- GPU/Common/FramebufferCommon.h | 4 + GPU/GLES/FramebufferManagerGLES.cpp | 101 +----------------------- GPU/GLES/FramebufferManagerGLES.h | 2 - ext/native/thin3d/VulkanQueueRunner.cpp | 2 +- 5 files changed, 96 insertions(+), 112 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 3291f94754..b06a5840b9 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1974,6 +1974,63 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { return true; } +// TODO: SSE/NEON +// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) +void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { + // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. + const u32 *src32 = (const u32 *)src; + + if (format == GE_FORMAT_8888) { + u32 *dst32 = (u32 *)dst; + if (src == dst) { + return; + } else { + // Here let's assume they don't intersect + for (u32 y = 0; y < height; ++y) { + memcpy(dst32, src32, width * 4); + src32 += srcStride; + dst32 += dstStride; + } + } + } else { + // But here it shouldn't matter if they do intersect + u16 *dst16 = (u16 *)dst; + switch (format) { + case GE_FORMAT_565: // BGR 565 + { + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGB565(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + } + break; + case GE_FORMAT_5551: // ABGR 1555 + { + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA5551(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + } + break; + case GE_FORMAT_4444: // ABGR 4444 + { + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA4444(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + } + break; + case GE_FORMAT_8888: + case GE_FORMAT_INVALID: + // Not possible. + break; + } + } +} + // This function takes an already correctly-sized framebuffer and packs it into RAM. // Does not need to account for scaling. // Color conversion is currently done on CPU but should theoretically be done on GPU. @@ -1986,19 +2043,43 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int return; } - const u32 fb_address = (0x04000000) | vfb->fb_address; + int possibleH = std::max(vfb->height - y, 0); + if (h > possibleH) { + h = possibleH; + } - Draw::DataFormat destFormat = GEFormatToThin3D(vfb->format); - const int dstBpp = (int)DataFormatSizeInBytes(destFormat); + // Pixel size always 4 here because we always request RGBA8888 + u32 bufSize = vfb->fb_stride * h * 4; + u32 fb_address = 0x04000000 | vfb->fb_address; - const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; - u8 *destPtr = Memory::GetPointer(fb_address + dstByteOffset); + bool convert = vfb->format != GE_FORMAT_8888; + const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; + const int packWidth = std::min(vfb->fb_stride, std::min(x + w, (int)vfb->width)); - // We always need to convert from the framebuffer native format. - // Right now that's always 8888. - DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address); + int dstByteOffset = y * vfb->fb_stride * dstBpp; + u8 *dst = Memory::GetPointer(fb_address + dstByteOffset); - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride); + u8 *packed = nullptr; + if (!convert) { + packed = (u8 *)dst; + } else { + // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address + if (!convBuf_ || convBufSize_ < bufSize) { + delete[] convBuf_; + convBuf_ = new u8[bufSize]; + convBufSize_ = bufSize; + } + packed = convBuf_; + } + + if (packed) { + DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); + int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, w, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packW); + if (convert) { + ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); + } + } } void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 62befe6913..552ae1cee1 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -402,6 +402,10 @@ protected: int bloomHack_ = 0; bool trueColor_ = false; + // Used to convert readbacks. + u8 *convBuf_ = nullptr; + u32 convBufSize_ = 0; + // Used by post-processing shaders std::vector extraFBOs_; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 3eb62e0bcd..376870a7a2 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -204,7 +204,6 @@ FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw) : FramebufferManagerCommon(draw), drawPixelsTex_(0), drawPixelsTexFormat_(GE_FORMAT_INVALID), - convBuf_(nullptr), draw2dprogram_(nullptr), postShaderProgram_(nullptr), stencilUploadProgram_(nullptr), @@ -721,63 +720,6 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, CHECK_GL_ERROR_IF_DEBUG(); } -// TODO: SSE/NEON -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) -void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { - // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. - const u32 *src32 = (const u32 *)src; - - if (format == GE_FORMAT_8888) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - // Here let's assume they don't intersect - for (u32 y = 0; y < height; ++y) { - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } - } - } else { - // But here it shouldn't matter if they do intersect - u16 *dst16 = (u16 *)dst; - switch (format) { - case GE_FORMAT_565: // BGR 565 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGB565(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_5551: // ABGR 1555 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA5551(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_4444: // ABGR 4444 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA4444(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_8888: - case GE_FORMAT_INVALID: - // Not possible. - break; - } - } -} - void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { CHECK_GL_ERROR_IF_DEBUG(); const int MAX_PBO = 2; @@ -900,48 +842,7 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { } void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - if (!vfb->fbo) { - ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0"); - return; - } - - int possibleH = std::max(vfb->height - y, 0); - if (h > possibleH) { - h = possibleH; - } - - // Pixel size always 4 here because we always request RGBA8888 - u32 bufSize = vfb->fb_stride * h * 4; - u32 fb_address = 0x04000000 | vfb->fb_address; - - bool convert = vfb->format != GE_FORMAT_8888; - const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; - const int packWidth = std::min(vfb->fb_stride, std::min(x + w, (int)vfb->width)); - - int dstByteOffset = y * vfb->fb_stride * dstBpp; - u8 *dst = Memory::GetPointer(fb_address + dstByteOffset); - - u8 *packed = nullptr; - if (!convert) { - packed = (u8 *)dst; - } else { - // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address - if (!convBuf_ || convBufSize_ < bufSize) { - delete [] convBuf_; - convBuf_ = new u8[bufSize]; - convBufSize_ = bufSize; - } - packed = convBuf_; - } - - if (packed) { - DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); - int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, packW, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packW); - if (convert) { - ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); - } - } + FramebufferManagerCommon::PackFramebufferSync_(vfb, x, y, w, h); // TODO: Move this into Thin3d. if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) { diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index f867f833d6..d5f922c852 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -112,8 +112,6 @@ private: int drawPixelsTexW_; int drawPixelsTexH_; - u8 *convBuf_; - u32 convBufSize_; GLSLProgram *draw2dprogram_; GLSLProgram *plainColorProgram_; GLSLProgram *postShaderProgram_; diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index ae4f668199..cadc947f5c 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -753,7 +753,7 @@ void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, int pixelStrid assert(res == VK_SUCCESS); for (int y = 0; y < height; y++) { - const uint8_t *src = (const uint8_t *)mappedData + width * y; + const uint8_t *src = (const uint8_t *)mappedData + width * pixelSize * y; uint8_t *dst = pixels + pixelStride * pixelSize * y; memcpy(dst, src, width * pixelSize); } From 9c254ea1c34da1aa021a81b57978b064fbfcb16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 29 Oct 2017 09:31:28 +0100 Subject: [PATCH 085/111] Revert most of the last commit - will do it differently. --- GPU/Common/FramebufferCommon.cpp | 99 +++------------------------ GPU/Common/FramebufferCommon.h | 4 -- GPU/GLES/FramebufferManagerGLES.cpp | 101 +++++++++++++++++++++++++++- GPU/GLES/FramebufferManagerGLES.h | 2 + 4 files changed, 111 insertions(+), 95 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index b06a5840b9..3291f94754 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1974,63 +1974,6 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { return true; } -// TODO: SSE/NEON -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) -void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { - // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. - const u32 *src32 = (const u32 *)src; - - if (format == GE_FORMAT_8888) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - // Here let's assume they don't intersect - for (u32 y = 0; y < height; ++y) { - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } - } - } else { - // But here it shouldn't matter if they do intersect - u16 *dst16 = (u16 *)dst; - switch (format) { - case GE_FORMAT_565: // BGR 565 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGB565(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_5551: // ABGR 1555 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA5551(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_4444: // ABGR 4444 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA4444(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_8888: - case GE_FORMAT_INVALID: - // Not possible. - break; - } - } -} - // This function takes an already correctly-sized framebuffer and packs it into RAM. // Does not need to account for scaling. // Color conversion is currently done on CPU but should theoretically be done on GPU. @@ -2043,43 +1986,19 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int return; } - int possibleH = std::max(vfb->height - y, 0); - if (h > possibleH) { - h = possibleH; - } + const u32 fb_address = (0x04000000) | vfb->fb_address; - // Pixel size always 4 here because we always request RGBA8888 - u32 bufSize = vfb->fb_stride * h * 4; - u32 fb_address = 0x04000000 | vfb->fb_address; + Draw::DataFormat destFormat = GEFormatToThin3D(vfb->format); + const int dstBpp = (int)DataFormatSizeInBytes(destFormat); - bool convert = vfb->format != GE_FORMAT_8888; - const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; - const int packWidth = std::min(vfb->fb_stride, std::min(x + w, (int)vfb->width)); + const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp; + u8 *destPtr = Memory::GetPointer(fb_address + dstByteOffset); - int dstByteOffset = y * vfb->fb_stride * dstBpp; - u8 *dst = Memory::GetPointer(fb_address + dstByteOffset); + // We always need to convert from the framebuffer native format. + // Right now that's always 8888. + DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address); - u8 *packed = nullptr; - if (!convert) { - packed = (u8 *)dst; - } else { - // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address - if (!convBuf_ || convBufSize_ < bufSize) { - delete[] convBuf_; - convBuf_ = new u8[bufSize]; - convBufSize_ = bufSize; - } - packed = convBuf_; - } - - if (packed) { - DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); - int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? - draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, w, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packW); - if (convert) { - ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); - } - } + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride); } void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 552ae1cee1..62befe6913 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -402,10 +402,6 @@ protected: int bloomHack_ = 0; bool trueColor_ = false; - // Used to convert readbacks. - u8 *convBuf_ = nullptr; - u32 convBufSize_ = 0; - // Used by post-processing shaders std::vector extraFBOs_; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 376870a7a2..3eb62e0bcd 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -204,6 +204,7 @@ FramebufferManagerGLES::FramebufferManagerGLES(Draw::DrawContext *draw) : FramebufferManagerCommon(draw), drawPixelsTex_(0), drawPixelsTexFormat_(GE_FORMAT_INVALID), + convBuf_(nullptr), draw2dprogram_(nullptr), postShaderProgram_(nullptr), stencilUploadProgram_(nullptr), @@ -720,6 +721,63 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, CHECK_GL_ERROR_IF_DEBUG(); } +// TODO: SSE/NEON +// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) +void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { + // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. + const u32 *src32 = (const u32 *)src; + + if (format == GE_FORMAT_8888) { + u32 *dst32 = (u32 *)dst; + if (src == dst) { + return; + } else { + // Here let's assume they don't intersect + for (u32 y = 0; y < height; ++y) { + memcpy(dst32, src32, width * 4); + src32 += srcStride; + dst32 += dstStride; + } + } + } else { + // But here it shouldn't matter if they do intersect + u16 *dst16 = (u16 *)dst; + switch (format) { + case GE_FORMAT_565: // BGR 565 + { + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGB565(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + } + break; + case GE_FORMAT_5551: // ABGR 1555 + { + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA5551(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + } + break; + case GE_FORMAT_4444: // ABGR 4444 + { + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA4444(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + } + break; + case GE_FORMAT_8888: + case GE_FORMAT_INVALID: + // Not possible. + break; + } + } +} + void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { CHECK_GL_ERROR_IF_DEBUG(); const int MAX_PBO = 2; @@ -842,7 +900,48 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { } void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { - FramebufferManagerCommon::PackFramebufferSync_(vfb, x, y, w, h); + if (!vfb->fbo) { + ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0"); + return; + } + + int possibleH = std::max(vfb->height - y, 0); + if (h > possibleH) { + h = possibleH; + } + + // Pixel size always 4 here because we always request RGBA8888 + u32 bufSize = vfb->fb_stride * h * 4; + u32 fb_address = 0x04000000 | vfb->fb_address; + + bool convert = vfb->format != GE_FORMAT_8888; + const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; + const int packWidth = std::min(vfb->fb_stride, std::min(x + w, (int)vfb->width)); + + int dstByteOffset = y * vfb->fb_stride * dstBpp; + u8 *dst = Memory::GetPointer(fb_address + dstByteOffset); + + u8 *packed = nullptr; + if (!convert) { + packed = (u8 *)dst; + } else { + // End result may be 16-bit but we are reading 32-bit, so there may not be enough space at fb_address + if (!convBuf_ || convBufSize_ < bufSize) { + delete [] convBuf_; + convBuf_ = new u8[bufSize]; + convBufSize_ = bufSize; + } + packed = convBuf_; + } + + if (packed) { + DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address); + int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about? + draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, packW, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packW); + if (convert) { + ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format); + } + } // TODO: Move this into Thin3d. if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) { diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index d5f922c852..f867f833d6 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -112,6 +112,8 @@ private: int drawPixelsTexW_; int drawPixelsTexH_; + u8 *convBuf_; + u32 convBufSize_; GLSLProgram *draw2dprogram_; GLSLProgram *plainColorProgram_; GLSLProgram *postShaderProgram_; From 93c785b76d41d838efabfaa5858e0128d28080cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 29 Oct 2017 10:56:02 +0100 Subject: [PATCH 086/111] Color-convert directly during the readback, saves a copy. Like we already do in D3D11. --- GPU/Vulkan/FramebufferVulkan.cpp | 1 + ext/native/thin3d/VulkanQueueRunner.cpp | 69 ++++++++++++++++++++--- ext/native/thin3d/VulkanQueueRunner.h | 2 +- ext/native/thin3d/VulkanRenderManager.cpp | 5 +- ext/native/thin3d/VulkanRenderManager.h | 2 +- ext/native/thin3d/thin3d_vulkan.cpp | 3 +- 6 files changed, 66 insertions(+), 16 deletions(-) diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index d6124cc467..84bb6faa80 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -387,6 +387,7 @@ void FramebufferManagerVulkan::RebindFramebuffer() { // Should this even happen? draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP }); } + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); } bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skipZero) { diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index cadc947f5c..734daa3ddd 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -1,3 +1,5 @@ +#include "Common/ColorConv.h" + #include "VulkanQueueRunner.h" #include "VulkanRenderManager.h" @@ -745,17 +747,66 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd // NOTE: Can't read the buffer using the CPU here - need to sync first. } -void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, int pixelStride, uint8_t *pixels) { +// Would love to share this with D3D11 but don't have a shared format enum we can use... as we don't want +// to depend on thin3d here. +// TODO: SSE/NEON +// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) +// Strides are in pixels. +void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, VkFormat format) { + // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. + const u32 *src32 = (const u32 *)src; + + if (format == VK_FORMAT_R8G8B8A8_UNORM) { + u32 *dst32 = (u32 *)dst; + if (src == dst) { + return; + } else { + for (u32 y = 0; y < height; ++y) { + memcpy(dst32, src32, width * 4); + src32 += srcStride; + dst32 += dstStride; + } + } + } else { + // But here it shouldn't matter if they do intersect + u16 *dst16 = (u16 *)dst; + switch (format) { + case VK_FORMAT_B5G6R5_UNORM_PACK16: // BGR 565 + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGB565(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case VK_FORMAT_A1R5G5B5_UNORM_PACK16: // ABGR 1555 + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA5551(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case VK_FORMAT_R4G4B4A4_UNORM_PACK16: // ABGR 4444 + for (u32 y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA4444(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + default: + Crash(); + // Not possible. + break; + } + } +} + +void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, VkFormat destFormat, int pixelStride, uint8_t *pixels) { // Read back to the requested address in ram from buffer. void *mappedData; - const int pixelSize = 4; // TODO: Fix. - VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, width * height * pixelSize, 0, &mappedData); - assert(res == VK_SUCCESS); + const int srcPixelSize = 4; // TODO: Fix. - for (int y = 0; y < height; y++) { - const uint8_t *src = (const uint8_t *)mappedData + width * pixelSize * y; - uint8_t *dst = pixels + pixelStride * pixelSize * y; - memcpy(dst, src, width * pixelSize); - } + VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, width * height * srcPixelSize, 0, &mappedData); + assert(res == VK_SUCCESS); + ConvertFromRGBA8888(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, destFormat); vkUnmapMemory(vulkan_->GetDevice(), readbackMemory_); } diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 1db5e1abea..477d361421 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -156,7 +156,7 @@ public: return (int)depth * 3 + (int)color; } - void CopyReadbackBuffer(int width, int height, int pixelStride, uint8_t *pixels); + void CopyReadbackBuffer(int width, int height, VkFormat destFormat, int pixelStride, uint8_t *pixels); private: void InitBackbufferRenderPass(); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 9728bcb8e6..7cf2c4bd11 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -327,7 +327,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); } -void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, uint8_t *pixels, int pixelStride) { +void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, VkFormat destFormat, uint8_t *pixels, int pixelStride) { VKRStep *step = new VKRStep{ VKRStepType::READBACK }; step->readback.aspectMask = aspectBits; step->readback.src = src; @@ -340,10 +340,9 @@ void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a FlushSync(); // Need to call this after FlushSyfnc so the pixels are guaranteed to be ready in CPU-accessible VRAM. - queueRunner_.CopyReadbackBuffer(w, h, pixelStride, pixels); + queueRunner_.CopyReadbackBuffer(w, h, destFormat, pixelStride, pixels); } - void VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) { VkResult U_ASSERT_ONLY res; // We share the same depth buffer but have multiple color buffers, see the loop below. diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 3a795f4b0c..063c37a1c0 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -85,7 +85,7 @@ public: void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); - void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, uint8_t *pixels, int pixelStride); + void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, VkFormat destFormat, uint8_t *pixels, int pixelStride); void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask); void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index e09503e043..b6a8161baa 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1292,13 +1292,12 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, int aspectMask = 0; if (channelBits & FBChannel::FB_COLOR_BIT) { - assert(vkFormat == src->GetFB()->color.format); aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; } if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, (uint8_t *)pixels, pixelStride); + renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, vkFormat, (uint8_t *)pixels, pixelStride); return true; } From 97dced543e182ca3abdba8fc84bf36b12bf5044e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 29 Oct 2017 14:42:51 +0100 Subject: [PATCH 087/111] Share the conversion code between vulkan and D3D, fixing the missing format thing. --- CMakeLists.txt | 3 +- ext/native/native.vcxproj | 3 +- ext/native/native.vcxproj.filters | 5 +- ext/native/thin3d/DataFormat.h | 71 +++++++++++++++++++++++ ext/native/thin3d/VulkanQueueRunner.cpp | 58 +----------------- ext/native/thin3d/VulkanQueueRunner.h | 4 +- ext/native/thin3d/VulkanRenderManager.cpp | 2 +- ext/native/thin3d/VulkanRenderManager.h | 4 +- ext/native/thin3d/thin3d.cpp | 55 +++++++++++++++++- ext/native/thin3d/thin3d.h | 63 +------------------- ext/native/thin3d/thin3d_d3d11.cpp | 50 ---------------- ext/native/thin3d/thin3d_vulkan.cpp | 4 +- 12 files changed, 142 insertions(+), 180 deletions(-) create mode 100644 ext/native/thin3d/DataFormat.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 30acdec62a..2135eb8ba2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -808,7 +808,8 @@ if(VULKAN) ext/native/thin3d/VulkanRenderManager.cpp ext/native/thin3d/VulkanRenderManager.h ext/native/thin3d/VulkanQueueRunner.cpp - ext/native/thin3d/VulkanQueueRunner.h) + ext/native/thin3d/VulkanQueueRunner.h + ext/native/thin3d/DataFormat.h) endif() if(WIN32) set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS} diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index 681a99b453..aa3b268a19 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -240,6 +240,7 @@ + @@ -760,4 +761,4 @@ - + \ No newline at end of file diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index 57aee62de4..6996479a53 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -326,6 +326,9 @@ thin3d + + thin3d + @@ -865,4 +868,4 @@ {06c6305a-a646-485b-85b9-645a24dd6553} - + \ No newline at end of file diff --git a/ext/native/thin3d/DataFormat.h b/ext/native/thin3d/DataFormat.h new file mode 100644 index 0000000000..fa9a8325be --- /dev/null +++ b/ext/native/thin3d/DataFormat.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +namespace Draw { + +enum class DataFormat : uint8_t { + UNDEFINED, + + R8_UNORM, + R8G8_UNORM, + R8G8B8_UNORM, + + R8G8B8A8_UNORM, + R8G8B8A8_UNORM_SRGB, + B8G8R8A8_UNORM, // D3D style + B8G8R8A8_UNORM_SRGB, // D3D style + + R8G8B8A8_SNORM, + R8G8B8A8_UINT, + R8G8B8A8_SINT, + + R4G4_UNORM_PACK8, + A4R4G4B4_UNORM_PACK16, // A4 in the UPPER bit + B4G4R4A4_UNORM_PACK16, + R4G4B4A4_UNORM_PACK16, + R5G6B5_UNORM_PACK16, + B5G6R5_UNORM_PACK16, + R5G5B5A1_UNORM_PACK16, // A1 in the LOWER bit + B5G5R5A1_UNORM_PACK16, // A1 in the LOWER bit + A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit. + + R16_FLOAT, + R16G16_FLOAT, + R16G16B16A16_FLOAT, + + R32_FLOAT, + R32G32_FLOAT, + R32G32B32_FLOAT, + R32G32B32A32_FLOAT, + + // Block compression formats. + // These are modern names for DXT and friends, now patent free. + // https://msdn.microsoft.com/en-us/library/bb694531.aspx + BC1_RGBA_UNORM_BLOCK, + BC1_RGBA_SRGB_BLOCK, + BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. Usually not worth using + BC2_SRGB_BLOCK, + BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color + BC3_SRGB_BLOCK, + BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha + BC4_SNORM_BLOCK, + BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha + BC5_SNORM_BLOCK, + BC6H_UFLOAT_BLOCK, // TODO + BC6H_SFLOAT_BLOCK, + BC7_UNORM_BLOCK, // Highly advanced, very expensive to compress, very good quality. + BC7_SRGB_BLOCK, + + ETC1, + + S8, + D16, + D24_S8, + D32F, + D32F_S8, +}; + +void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format); + +} // namespace diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 734daa3ddd..f4028a0bfa 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -1,5 +1,4 @@ -#include "Common/ColorConv.h" - +#include "DataFormat.h" #include "VulkanQueueRunner.h" #include "VulkanRenderManager.h" @@ -747,60 +746,7 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd // NOTE: Can't read the buffer using the CPU here - need to sync first. } -// Would love to share this with D3D11 but don't have a shared format enum we can use... as we don't want -// to depend on thin3d here. -// TODO: SSE/NEON -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) -// Strides are in pixels. -void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, VkFormat format) { - // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. - const u32 *src32 = (const u32 *)src; - - if (format == VK_FORMAT_R8G8B8A8_UNORM) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - for (u32 y = 0; y < height; ++y) { - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } - } - } else { - // But here it shouldn't matter if they do intersect - u16 *dst16 = (u16 *)dst; - switch (format) { - case VK_FORMAT_B5G6R5_UNORM_PACK16: // BGR 565 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGB565(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case VK_FORMAT_A1R5G5B5_UNORM_PACK16: // ABGR 1555 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA5551(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case VK_FORMAT_R4G4B4A4_UNORM_PACK16: // ABGR 4444 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA4444(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - default: - Crash(); - // Not possible. - break; - } - } -} - -void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, VkFormat destFormat, int pixelStride, uint8_t *pixels) { +void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { // Read back to the requested address in ram from buffer. void *mappedData; const int srcPixelSize = 4; // TODO: Fix. diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 477d361421..05accdf6a5 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -4,7 +4,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "math/dataconv.h" -#include "thin3d/thin3d.h" +#include "thin3d/DataFormat.h" class VKRFramebuffer; struct VKRImage; @@ -156,7 +156,7 @@ public: return (int)depth * 3 + (int)color; } - void CopyReadbackBuffer(int width, int height, VkFormat destFormat, int pixelStride, uint8_t *pixels); + void CopyReadbackBuffer(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels); private: void InitBackbufferRenderPass(); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 7cf2c4bd11..e74eb71853 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -327,7 +327,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight(); } -void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, VkFormat destFormat, uint8_t *pixels, int pixelStride) { +void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) { VKRStep *step = new VKRStep{ VKRStepType::READBACK }; step->readback.aspectMask = aspectBits; step->readback.src = src; diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 063c37a1c0..717539cee0 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -11,7 +11,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "math/dataconv.h" -#include "thin3d/thin3d.h" +#include "thin3d/DataFormat.h" #include "thin3d/VulkanQueueRunner.h" // Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine @@ -85,7 +85,7 @@ public: void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); - void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, VkFormat destFormat, uint8_t *pixels, int pixelStride); + void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask); void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter); diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 1de3bbba4d..39bdc447bc 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -1,7 +1,9 @@ #include -#include +#include #include "base/logging.h" +#include "thin3d/thin3d.h" +#include "Common/ColorConv.h" namespace Draw { @@ -320,4 +322,55 @@ DrawContext::~DrawContext() { } } +// TODO: SSE/NEON +// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) +void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { + // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. + const uint32_t *src32 = (const uint32_t *)src; + + if (format == Draw::DataFormat::R8G8B8A8_UNORM) { + uint32_t *dst32 = (uint32_t *)dst; + if (src == dst) { + return; + } else { + for (uint32_t y = 0; y < height; ++y) { + memcpy(dst32, src32, width * 4); + src32 += srcStride; + dst32 += dstStride; + } + } + } else { + // But here it shouldn't matter if they do intersect + uint16_t *dst16 = (uint16_t *)dst; + switch (format) { + case Draw::DataFormat::R5G6B5_UNORM_PACK16: // BGR 565 + for (uint32_t y = 0; y < height; ++y) { + ConvertRGBA8888ToRGB565(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case Draw::DataFormat::A1R5G5B5_UNORM_PACK16: // ABGR 1555 + for (uint32_t y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA5551(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case Draw::DataFormat::A4R4G4B4_UNORM_PACK16: // ABGR 4444 + for (uint32_t y = 0; y < height; ++y) { + ConvertRGBA8888ToRGBA4444(dst16, src32, width); + src32 += srcStride; + dst16 += dstStride; + } + break; + case Draw::DataFormat::R8G8B8A8_UNORM: + case Draw::DataFormat::UNDEFINED: + // Not possible. + break; + } + } +} + + } // namespace Draw diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 6b3a6dfac9..0dead1552a 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -12,6 +12,7 @@ #include #include "base/logging.h" +#include "DataFormat.h" class Matrix4x4; @@ -176,68 +177,6 @@ enum class TextureType : uint8_t { ARRAY2D, }; -enum class DataFormat : uint8_t { - UNDEFINED, - - R8_UNORM, - R8G8_UNORM, - R8G8B8_UNORM, - - R8G8B8A8_UNORM, - R8G8B8A8_UNORM_SRGB, - B8G8R8A8_UNORM, // D3D style - B8G8R8A8_UNORM_SRGB, // D3D style - - R8G8B8A8_SNORM, - R8G8B8A8_UINT, - R8G8B8A8_SINT, - - R4G4_UNORM_PACK8, - A4R4G4B4_UNORM_PACK16, // A4 in the UPPER bit - B4G4R4A4_UNORM_PACK16, - R4G4B4A4_UNORM_PACK16, - R5G6B5_UNORM_PACK16, - B5G6R5_UNORM_PACK16, - R5G5B5A1_UNORM_PACK16, // A1 in the LOWER bit - B5G5R5A1_UNORM_PACK16, // A1 in the LOWER bit - A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit. - - R16_FLOAT, - R16G16_FLOAT, - R16G16B16A16_FLOAT, - - R32_FLOAT, - R32G32_FLOAT, - R32G32B32_FLOAT, - R32G32B32A32_FLOAT, - - // Block compression formats. - // These are modern names for DXT and friends, now patent free. - // https://msdn.microsoft.com/en-us/library/bb694531.aspx - BC1_RGBA_UNORM_BLOCK, - BC1_RGBA_SRGB_BLOCK, - BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. Usually not worth using - BC2_SRGB_BLOCK, - BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color - BC3_SRGB_BLOCK, - BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha - BC4_SNORM_BLOCK, - BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha - BC5_SNORM_BLOCK, - BC6H_UFLOAT_BLOCK, // TODO - BC6H_SFLOAT_BLOCK, - BC7_UNORM_BLOCK, // Highly advanced, very expensive to compress, very good quality. - BC7_SRGB_BLOCK, - - ETC1, - - S8, - D16, - D24_S8, - D32F, - D32F_S8, -}; - enum class ShaderStage { VERTEX, FRAGMENT, diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index a40d4c4bfc..72e73fe951 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -1310,56 +1310,6 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, return false; } -// TODO: SSE/NEON -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) -void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, Draw::DataFormat format) { - // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. - const u32 *src32 = (const u32 *)src; - - if (format == Draw::DataFormat::R8G8B8A8_UNORM) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - for (u32 y = 0; y < height; ++y) { - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } - } - } else { - // But here it shouldn't matter if they do intersect - u16 *dst16 = (u16 *)dst; - switch (format) { - case Draw::DataFormat::R5G6B5_UNORM_PACK16: // BGR 565 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGB565(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case Draw::DataFormat::A1R5G5B5_UNORM_PACK16: // ABGR 1555 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA5551(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case Draw::DataFormat::A4R4G4B4_UNORM_PACK16: // ABGR 4444 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA4444(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case Draw::DataFormat::R8G8B8A8_UNORM: - case Draw::DataFormat::UNDEFINED: - // Not possible. - break; - } - } -} - bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat format, void *pixels, int pixelStride) { D3D11Framebuffer *fb = (D3D11Framebuffer *)src; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b6a8161baa..9ea34596c7 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1288,8 +1288,6 @@ bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int sr bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) { VKFramebuffer *src = (VKFramebuffer *)srcfb; - VkFormat vkFormat = DataFormatToVulkan(format); - int aspectMask = 0; if (channelBits & FBChannel::FB_COLOR_BIT) { aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; @@ -1297,7 +1295,7 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, vkFormat, (uint8_t *)pixels, pixelStride); + renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride); return true; } From 6e9cd933b2f322a2cd6350d2680ff07347d52bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 30 Oct 2017 16:30:19 +0100 Subject: [PATCH 088/111] Implement GetFramebufferAPITexture for Vulkan --- ext/native/thin3d/VulkanRenderManager.cpp | 3 ++- ext/native/thin3d/thin3d_vulkan.cpp | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index e74eb71853..6662f5ca6d 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -515,7 +515,8 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; break; } - else { + else if (steps_[i]->render.finalColorLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + Crash(); // May need to shadow the framebuffer if we re-order passes later. } } diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 9ea34596c7..b031b89e76 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1315,11 +1315,28 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne if (channelBit & FBChannel::FB_COLOR_BIT) aspect |= VK_IMAGE_ASPECT_COLOR_BIT; if (channelBit & FBChannel::FB_DEPTH_BIT) aspect |= VK_IMAGE_ASPECT_DEPTH_BIT; if (channelBit & FBChannel::FB_STENCIL_BIT) aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; + + ILOG("Binding image as texture: %x", (int)fb->GetFB()->color.image); + boundImageView_[0] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); } uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { - return 0; + if (!fbo) + return 0; + + VKFramebuffer *fb = (VKFramebuffer *)fbo; + VkImageView view = VK_NULL_HANDLE; + switch (channelBit) { + case FB_COLOR_BIT: + view = fb->GetFB()->color.imageView; + break; + case FB_DEPTH_BIT: + case FB_STENCIL_BIT: + view = fb->GetFB()->depth.imageView; + break; + } + return (uintptr_t)view; } void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { From 6253280fba6b17fa90826c7e2e93baa77d699897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 29 Oct 2017 22:17:27 +0100 Subject: [PATCH 089/111] Delete superfluous bind of temporary render target on creation. --- GPU/Common/FramebufferCommon.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 3291f94754..1f4eb712b1 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1806,8 +1806,6 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(u16 w, u16 h, Draw::FBCo if (!fbo) return fbo; - // TODO: Move binding out of here! - draw_->BindFramebufferAsRenderTarget(fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); const TempFBO info = { fbo, gpuStats.numFlips }; tempFBOs_[key] = info; return fbo; From 65e23bb9f350eefdc489ef14b08e8daa8074fa32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 30 Oct 2017 12:05:08 +0100 Subject: [PATCH 090/111] Some reorganization. Start implementing framebuffer depal for Vulkan. --- GPU/Common/DepalettizeShaderCommon.h | 2 + GPU/D3D11/DepalettizeShaderD3D11.cpp | 2 - GPU/GLES/DepalettizeShaderGLES.cpp | 2 - GPU/Vulkan/DepalettizeShaderVulkan.cpp | 113 ++++++++++++++++++++++--- GPU/Vulkan/DepalettizeShaderVulkan.h | 39 ++++++--- GPU/Vulkan/FramebufferVulkan.cpp | 95 ++++++--------------- GPU/Vulkan/FramebufferVulkan.h | 26 +++--- GPU/Vulkan/GPU_Vulkan.cpp | 53 +++++++++++- GPU/Vulkan/GPU_Vulkan.h | 14 +++ GPU/Vulkan/TextureCacheVulkan.cpp | 82 +++++++++--------- GPU/Vulkan/TextureCacheVulkan.h | 7 ++ GPU/Vulkan/VulkanUtil.cpp | 12 ++- GPU/Vulkan/VulkanUtil.h | 9 +- 13 files changed, 295 insertions(+), 161 deletions(-) diff --git a/GPU/Common/DepalettizeShaderCommon.h b/GPU/Common/DepalettizeShaderCommon.h index 7d2b02d113..933d4c02d9 100644 --- a/GPU/Common/DepalettizeShaderCommon.h +++ b/GPU/Common/DepalettizeShaderCommon.h @@ -20,4 +20,6 @@ #include "GPU/ge_constants.h" #include "GPU/Common/ShaderCommon.h" +static const int DEPAL_TEXTURE_OLD_AGE = 120; + void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguage language); diff --git a/GPU/D3D11/DepalettizeShaderD3D11.cpp b/GPU/D3D11/DepalettizeShaderD3D11.cpp index 59c558f7df..1ab84dbd38 100644 --- a/GPU/D3D11/DepalettizeShaderD3D11.cpp +++ b/GPU/D3D11/DepalettizeShaderD3D11.cpp @@ -29,8 +29,6 @@ #include "GPU/D3D11/D3D11Util.h" #include "GPU/Common/DepalettizeShaderCommon.h" -static const int DEPAL_TEXTURE_OLD_AGE = 120; - #ifdef _WIN32 #define SHADERLOG #endif diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index f18513753a..b6f5c2e7d6 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -26,8 +26,6 @@ #include "ext/native/gfx/GLStateCache.h" #include "GPU/Common/DepalettizeShaderCommon.h" -static const int DEPAL_TEXTURE_OLD_AGE = 120; - #ifdef _WIN32 #define SHADERLOG #endif diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index 2de073cabe..c785ee4372 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -15,10 +15,29 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - +#include "Common/Vulkan/VulkanContext.h" +#include "GPU/GPUState.h" +#include "GPU/Common/DepalettizeShaderCommon.h" #include "GPU/Vulkan/DepalettizeShaderVulkan.h" +#include "GPU/Vulkan/VulkanUtil.h" +#include "Common/Vulkan/VulkanImage.h" -DepalShaderCacheVulkan::DepalShaderCacheVulkan() { +static VkFormat GetClutDestFormat(GEPaletteFormat format) { + switch (format) { + case GE_CMODE_16BIT_ABGR4444: + return VK_FORMAT_R4G4B4A4_UNORM_PACK16; + case GE_CMODE_16BIT_ABGR5551: + return VK_FORMAT_A1R5G5B5_UNORM_PACK16; + case GE_CMODE_16BIT_BGR5650: + return VK_FORMAT_R5G6B5_UNORM_PACK16; + case GE_CMODE_32BIT_ABGR8888: + return VK_FORMAT_R8G8B8A8_UNORM; + } + return VK_FORMAT_UNDEFINED; +} + +DepalShaderCacheVulkan::DepalShaderCacheVulkan(Draw::DrawContext *draw, VulkanContext *vulkan) + : draw_(draw), vulkan_(vulkan) { } @@ -26,22 +45,94 @@ DepalShaderCacheVulkan::~DepalShaderCacheVulkan() { } -DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) { +DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(uint32_t clutMode, GEBufferFormat pixelFormat) { + u32 id = GenerateShaderID(clutMode, pixelFormat); + + auto shader = cache_.find(id); + if (shader != cache_.end()) { + return shader->second; + } + + char *buffer = new char[2048]; + + VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS); + + GenerateDepalShader(buffer, pixelFormat, GLSL_VULKAN); + + std::string error; + VkShaderModule fshader = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, buffer, &error); + if (fshader == VK_NULL_HANDLE) { + return nullptr; + } + + VkPipeline pipeline = vulkan2D_->GetPipeline(rp, vshader_, fshader); + // Can delete the shader module now that the pipeline has been created. + // Maybe don't even need to queue it.. + vulkan_->Delete().QueueDeleteShaderModule(fshader); + + DepalShaderVulkan *depal = new DepalShaderVulkan(); + depal->pipeline = pipeline; + depal->code = buffer; + cache_[id] = depal; return nullptr; } -VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut) { - return nullptr; +VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat, u32 clutID, u32 *rawClut) { + const u32 realClutID = clutID ^ clutFormat; + + auto oldtex = texCache_.find(realClutID); + if (oldtex != texCache_.end()) { + oldtex->second->lastFrame = gpuStats.numFlips; + return oldtex->second->texture; + } + + VkFormat destFormat = GetClutDestFormat(clutFormat); + int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512; + + VkBuffer pushBuffer; + uint32_t pushOffset = push_->PushAligned(rawClut, 1024, 4, &pushBuffer); + + VulkanTexture *vktex = new VulkanTexture(vulkan_, alloc_); + vktex->CreateDirect(cmd_, texturePixels, 1, 1, destFormat, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT, nullptr); + vktex->UploadMip(cmd_, 0, texturePixels, 1, pushBuffer, pushOffset, texturePixels); + vktex->EndCreate(cmd_); + + DepalTextureVulkan *tex = new DepalTextureVulkan(); + tex->texture = vktex; + tex->lastFrame = gpuStats.numFlips; + texCache_[realClutID] = tex; + return tex->texture; } -void DepalShaderCacheVulkan::Clear() {} +void DepalShaderCacheVulkan::Clear() { + for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) { + // Delete the shader/pipeline too. + delete shader->second; + } + cache_.clear(); -void DepalShaderCacheVulkan::Decimate() {} + for (auto tex = texCache_.begin(); tex != texCache_.end(); ++tex) { + delete tex->second->texture; + delete tex->second; + } + texCache_.clear(); -u32 DepalShaderCacheVulkan::GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) { - return 0; + // TODO: Delete vertex shader. } -bool DepalShaderCacheVulkan::CreateVertexShader() { - return false; +void DepalShaderCacheVulkan::Decimate() { + // We don't bother decimating the generated shaders, there are never very many of them. + for (auto tex = texCache_.begin(); tex != texCache_.end(); ) { + if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.numFlips) { + delete tex->second->texture; + delete tex->second; + texCache_.erase(tex++); + } else { + ++tex; + } + } +} + +u32 DepalShaderCacheVulkan::GenerateShaderID(uint32_t clutMode, GEBufferFormat pixelFormat) { + return (clutMode & 0xFFFFFF) | (pixelFormat << 24); } diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.h b/GPU/Vulkan/DepalettizeShaderVulkan.h index 63faa1c8b6..fb2c3db4a4 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.h +++ b/GPU/Vulkan/DepalettizeShaderVulkan.h @@ -20,42 +20,59 @@ #include #include "Common/CommonTypes.h" +#include "Common/Vulkan/VulkanContext.h" +#include "Common/Vulkan/VulkanImage.h" +#include "Common/Vulkan/VulkanMemory.h" #include "GPU/ge_constants.h" +#include "thin3d/thin3d.h" class DepalShaderVulkan { public: - /* - GLuint program; - GLuint fragShader; - GLint a_position; - GLint a_texcoord0; - */ + ~DepalShaderVulkan() { + delete[] code; + } + // A Vulkan2D pipeline. Set texture to slot 0 and palette texture to slot 1. + VkPipeline pipeline; + const char *code = nullptr;; }; class DepalTextureVulkan { public: - int texture; + VulkanTexture *texture; int lastFrame; }; class VulkanTexture; +class Vulkan2D; // Caches both shaders and palette textures. // Could even avoid bothering with palette texture and just use uniform data... class DepalShaderCacheVulkan { public: - DepalShaderCacheVulkan(); + DepalShaderCacheVulkan(Draw::DrawContext *draw, VulkanContext *vulkan); ~DepalShaderCacheVulkan(); // This also uploads the palette and binds the correct texture. - DepalShaderVulkan *GetDepalettizeShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat); + DepalShaderVulkan *GetDepalettizeShader(uint32_t clutMode, GEBufferFormat pixelFormat); VulkanTexture *GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut); void Clear(); void Decimate(); + void SetVulkan2D(Vulkan2D *vk2d) { vulkan2D_ = vk2d; } + void SetPushBuffer(VulkanPushBuffer *push) { push_ = push; } + void SetAllocator(VulkanDeviceAllocator *alloc) { alloc_ = alloc; } + void SetVShader(VkShaderModule vshader) { vshader_ = vshader; } + private: - u32 GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat); - bool CreateVertexShader(); + u32 GenerateShaderID(uint32_t clutMode, GEBufferFormat pixelFormat); + + VkCommandBuffer cmd_ = VK_NULL_HANDLE; + Draw::DrawContext *draw_ = nullptr; + VulkanContext *vulkan_ = nullptr; + VulkanPushBuffer *push_ = nullptr; + VulkanDeviceAllocator *alloc_ = nullptr; + VkShaderModule vshader_ = VK_NULL_HANDLE; + Vulkan2D *vulkan2D_ = nullptr; // GLuint vertexShader_; std::map cache_; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 84bb6faa80..4bf271f7e6 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -88,9 +88,8 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk convBufSize_(0), textureCacheVulkan_(nullptr), shaderManagerVulkan_(nullptr), - curFrame_(0), pipelinePostShader_(VK_NULL_HANDLE), - vulkan2D_(vulkan) { + depalVulkan_(draw, vulkan) { InitDeviceObjects(); @@ -101,7 +100,6 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk FramebufferManagerVulkan::~FramebufferManagerVulkan() { delete[] convBuf_; - vulkan2D_.Shutdown(); DestroyDeviceObjects(); } @@ -121,23 +119,12 @@ void FramebufferManagerVulkan::SetDrawEngine(DrawEngineVulkan *td) { } void FramebufferManagerVulkan::InitDeviceObjects() { - // Initialize framedata - for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { - frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024); - } - - pipelineCache2D_ = vulkan_->CreatePipelineCache(); - std::string fs_errors, vs_errors; fsBasicTex_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, tex_fs, &fs_errors); vsBasicTex_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, tex_vs, &vs_errors); assert(fsBasicTex_ != VK_NULL_HANDLE); assert(vsBasicTex_ != VK_NULL_HANDLE); - // Prime the 2D pipeline cache. - // vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); - // vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS), vsBasicTex_, fsBasicTex_); - VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; @@ -153,13 +140,6 @@ void FramebufferManagerVulkan::InitDeviceObjects() { } void FramebufferManagerVulkan::DestroyDeviceObjects() { - for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { - if (frameData_[i].push_) { - frameData_[i].push_->Destroy(vulkan_); - delete frameData_[i].push_; - frameData_[i].push_ = nullptr; - } - } delete drawPixelsTex_; drawPixelsTex_ = nullptr; @@ -172,32 +152,29 @@ void FramebufferManagerVulkan::DestroyDeviceObjects() { vulkan_->Delete().QueueDeleteSampler(linearSampler_); if (nearestSampler_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteSampler(nearestSampler_); - // pipelineBasicTex_ and pipelineBasicTex_ come from vulkan2D_. - if (pipelineCache2D_ != VK_NULL_HANDLE) - vulkan_->Delete().QueueDeletePipelineCache(pipelineCache2D_); } void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth) { - float x, y, w, h; - CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); + float x, y, w, h; + CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL); - int mask = 0; - // The Clear detection takes care of doing a regular draw instead if separate masking - // of color and alpha is needed, so we can just treat them as the same. - if (clearColor || clearAlpha) - mask |= Draw::FBChannel::FB_COLOR_BIT; - if (clearDepth) - mask |= Draw::FBChannel::FB_DEPTH_BIT; - if (clearAlpha) - mask |= Draw::FBChannel::FB_STENCIL_BIT; + int mask = 0; + // The Clear detection takes care of doing a regular draw instead if separate masking + // of color and alpha is needed, so we can just treat them as the same. + if (clearColor || clearAlpha) + mask |= Draw::FBChannel::FB_COLOR_BIT; + if (clearDepth) + mask |= Draw::FBChannel::FB_DEPTH_BIT; + if (clearAlpha) + mask |= Draw::FBChannel::FB_STENCIL_BIT; - draw_->Clear(mask, color, depth, 0); - if (clearColor || clearAlpha) { - SetColorUpdated(gstate_c.skipDrawReason); - } - if (clearDepth) { - SetDepthUpdated(); - } + draw_->Clear(mask, color, depth, 0); + if (clearColor || clearAlpha) { + SetColorUpdated(gstate_c.skipDrawReason); + } + if (clearDepth) { + SetDepthUpdated(); + } } void FramebufferManagerVulkan::UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight) { @@ -242,6 +219,7 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor // Initialize backbuffer texture for DrawPixels drawPixelsTexFormat_ = srcPixelFormat; } else { + // TODO: We may want to double-buffer these, when we have more frames hanging about. drawPixelsTex_->TransitionForUpload(initCmd); } @@ -298,7 +276,7 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor } VkBuffer buffer; - size_t offset = frameData_[curFrame_].push_->Push(data, width * height * 4, &buffer); + size_t offset = push_->Push(data, width * height * 4, &buffer); drawPixelsTex_->UploadMip(initCmd, 0, width, height, buffer, (uint32_t)offset, width); drawPixelsTex_->EndCreate(initCmd); @@ -357,22 +335,21 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa // TODO: Should probably use draw_ directly and not go low level - VulkanPushBuffer *push = frameData_[curFrame_].push_; VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); if ((flags & DRAWTEX_KEEP_TEX) == 0) overrideImageView_ = VK_NULL_HANDLE; - VkDescriptorSet descSet = vulkan2D_.GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); + VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); VkBuffer vbuffer; - VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer); + VkDeviceSize offset = push_->Push(vtx, sizeof(vtx), &vbuffer); renderManager->BindPipeline(cur2DPipeline_); - renderManager->Draw(vulkan2D_.GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); + renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4); } void FramebufferManagerVulkan::Bind2DShader() { VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS); - cur2DPipeline_ = vulkan2D_.GetPipeline(pipelineCache2D_, rp, vsBasicTex_, fsBasicTex_); + cur2DPipeline_ = vulkan2D_->GetPipeline(rp, vsBasicTex_, fsBasicTex_); } void FramebufferManagerVulkan::BindPostShader(const PostShaderUniforms &uniforms) { @@ -608,31 +585,13 @@ void ConvertFromRGBA8888_Vulkan(u8 *dst, const u8 *src, u32 dstStride, u32 srcSt void FramebufferManagerVulkan::BeginFrameVulkan() { BeginFrame(); - - vulkan2D_.BeginFrame(); - - FrameData &frame = frameData_[curFrame_]; - - frame.push_->Reset(); - frame.push_->Begin(vulkan_); } void FramebufferManagerVulkan::EndFrame() { - // We flush to memory last requested framebuffer, if any. - // Only do this in the read-framebuffer modes. - FrameData &frame = frameData_[curFrame_]; - frame.push_->End(); - - vulkan2D_.EndFrame(); - - curFrame_++; - if (curFrame_ >= vulkan_->GetInflightFrames()) { - curFrame_ = 0; - } } void FramebufferManagerVulkan::DeviceLost() { - vulkan2D_.DeviceLost(); + vulkan2D_->DeviceLost(); DestroyAllFBOs(); DestroyDeviceObjects(); @@ -641,7 +600,7 @@ void FramebufferManagerVulkan::DeviceLost() { void FramebufferManagerVulkan::DeviceRestore(VulkanContext *vulkan) { vulkan_ = vulkan; - vulkan2D_.DeviceRestore(vulkan_); + vulkan2D_->DeviceRestore(vulkan_); InitDeviceObjects(); } diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 46b76e49db..2ea5037c6b 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -22,6 +22,7 @@ #include "GPU/GPUInterface.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Vulkan/VulkanUtil.h" +#include "GPU/Vulkan/DepalettizeShaderVulkan.h" // TODO: Remove? enum VulkanFBOColorDepth { @@ -44,6 +45,8 @@ R"( vec2 texelDelta; vec4 time; )"; +class VulkanPushBuffer; + class FramebufferManagerVulkan : public FramebufferManagerCommon { public: FramebufferManagerVulkan(Draw::DrawContext *draw, VulkanContext *vulkan); @@ -52,6 +55,8 @@ public: void SetTextureCache(TextureCacheVulkan *tc); void SetShaderManager(ShaderManagerVulkan *sm); void SetDrawEngine(DrawEngineVulkan *td); + void SetVulkan2D(Vulkan2D *vk2d) { vulkan2D_ = vk2d; } + void SetPushBuffer(VulkanPushBuffer *push) { push_ = push; } // x,y,w,h are relative to destW, destH which fill out the target completely. void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) override; @@ -105,28 +110,21 @@ private: // Used to keep track of command buffers here but have moved all that into Thin3D. // Used by DrawPixels - VulkanTexture *drawPixelsTex_; - GEBufferFormat drawPixelsTexFormat_; - - u8 *convBuf_; - u32 convBufSize_; + VulkanTexture *drawPixelsTex_ = nullptr; + GEBufferFormat drawPixelsTexFormat_ = GE_FORMAT_INVALID; + u8 *convBuf_ = nullptr; + u32 convBufSize_ = 0; TextureCacheVulkan *textureCacheVulkan_; ShaderManagerVulkan *shaderManagerVulkan_; DrawEngineVulkan *drawEngineVulkan_; + VulkanPushBuffer *push_; + DepalShaderCacheVulkan depalVulkan_; enum { MAX_COMMAND_BUFFERS = 32, }; - // Commandbuffers are handled internally in thin3d, one for each framebuffer pass. - struct FrameData { - VulkanPushBuffer *push_; - }; - - FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; - int curFrame_; - // This gets copied to the current frame's push buffer as needed. PostShaderUniforms postUniforms_; @@ -148,5 +146,5 @@ private: VkImageView overrideImageView_ = VK_NULL_HANDLE; // Simple 2D drawing engine. - Vulkan2D vulkan2D_; + Vulkan2D *vulkan2D_; }; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index f1fb20e9bc..3cb01856d1 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -76,7 +76,9 @@ static const VulkanCommandTableEntry commandTable[] = { GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : GPUCommon(gfxCtx, draw), vulkan_((VulkanContext *)gfxCtx->GetAPIContext()), - drawEngine_(vulkan_, draw) { + drawEngine_(vulkan_, draw), + depalShaderCache_(draw, vulkan_), + vulkan2D_(vulkan_) { UpdateVsyncInterval(true); CheckGPUFeatures(); @@ -97,10 +99,14 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) framebufferManagerVulkan_->SetTextureCache(textureCacheVulkan_); framebufferManagerVulkan_->SetDrawEngine(&drawEngine_); framebufferManagerVulkan_->SetShaderManager(shaderManagerVulkan_); - textureCacheVulkan_->SetFramebufferManager(framebufferManagerVulkan_); + framebufferManagerVulkan_->SetVulkan2D(&vulkan2D_); textureCacheVulkan_->SetDepalShaderCache(&depalShaderCache_); + textureCacheVulkan_->SetFramebufferManager(framebufferManagerVulkan_); textureCacheVulkan_->SetShaderManager(shaderManagerVulkan_); textureCacheVulkan_->SetDrawEngine(&drawEngine_); + textureCacheVulkan_->SetVulkan2D(&vulkan2D_); + + InitDeviceObjects(); // Sanity check gstate if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) { @@ -155,7 +161,9 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) } GPU_Vulkan::~GPU_Vulkan() { + DestroyDeviceObjects(); framebufferManagerVulkan_->DestroyAllFBOs(); + vulkan2D_.Shutdown(); depalShaderCache_.Clear(); delete textureCacheVulkan_; delete pipelineManager_; @@ -209,9 +217,19 @@ void GPU_Vulkan::BeginHostFrame() { resized_ = false; textureCacheVulkan_->StartFrame(); - depalShaderCache_.Decimate(); + + + FrameData &frame = frameData_[curFrame_]; + + frame.push_->Reset(); + frame.push_->Begin(vulkan_); framebufferManagerVulkan_->BeginFrameVulkan(); + framebufferManagerVulkan_->SetPushBuffer(frameData_[curFrame_].push_); + depalShaderCache_.SetPushBuffer(frameData_[curFrame_].push_); + textureCacheVulkan_->SetPushBuffer(frameData_[curFrame_].push_); + + vulkan2D_.BeginFrame(); shaderManagerVulkan_->DirtyShader(); gstate_c.Dirty(DIRTY_ALL); @@ -226,6 +244,16 @@ void GPU_Vulkan::BeginHostFrame() { } void GPU_Vulkan::EndHostFrame() { + FrameData &frame = frameData_[curFrame_]; + frame.push_->End(); + + vulkan2D_.EndFrame(); + + curFrame_++; + if (curFrame_ >= vulkan_->GetInflightFrames()) { + curFrame_ = 0; + } + drawEngine_.EndFrame(); framebufferManagerVulkan_->EndFrame(); textureCacheVulkan_->EndFrame(); @@ -769,7 +797,26 @@ void GPU_Vulkan::FastLoadBoneMatrix(u32 target) { gstate.FastLoadBoneMatrix(target); } +void GPU_Vulkan::InitDeviceObjects() { + // Initialize framedata + for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { + frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024); + } +} + +void GPU_Vulkan::DestroyDeviceObjects() { + for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { + if (frameData_[i].push_) { + frameData_[i].push_->Destroy(vulkan_); + delete frameData_[i].push_; + frameData_[i].push_ = nullptr; + } + } +} + void GPU_Vulkan::DeviceLost() { + DestroyDeviceObjects(); + framebufferManagerVulkan_->DeviceLost(); drawEngine_.DeviceLost(); pipelineManager_->DeviceLost(); diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index 1e7dc06ec2..ccdf36b3a4 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -101,6 +101,10 @@ private: void ReinitializeInternal() override; inline void UpdateVsyncInterval(bool force); void UpdateCmdInfo(); + + void InitDeviceObjects(); + void DestroyDeviceObjects(); + static CommandInfo cmdInfo_[256]; VulkanContext *vulkan_; @@ -121,4 +125,14 @@ private: std::string reportingPrimaryInfo_; std::string reportingFullInfo_; + + // Simple 2D drawing engine. + Vulkan2D vulkan2D_; + + struct FrameData { + VulkanPushBuffer *push_; + }; + + FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]{}; + int curFrame_ = 0; }; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 2ae17c63b1..ce5124aaa1 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -152,6 +152,11 @@ void TextureCacheVulkan::SetFramebufferManager(FramebufferManagerVulkan *fbManag framebufferManager_ = fbManager; } +void TextureCacheVulkan::SetVulkan2D(Vulkan2D *vk2d) { + vulkan2D_ = vk2d; + depalShaderCache_->SetVulkan2D(vk2d); +} + void TextureCacheVulkan::DeviceLost() { Clear(true); @@ -269,6 +274,8 @@ void TextureCacheVulkan::SetFramebufferSamplingParams(u16 bufferWidth, u16 buffe void TextureCacheVulkan::StartFrame() { InvalidateLastTexture(); + depalShaderCache_->Decimate(); + timesInvalidatedAllThisFrame_ = 0; texelsScaledThisFrame_ = 0; @@ -343,42 +350,24 @@ void TextureCacheVulkan::Unbind() { } void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { - DepalShaderVulkan *depal = nullptr; + DepalShaderVulkan *depalShader = nullptr; const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { - // depal = depalShaderCache_->GetDepalettizeShader(clutFormat, framebuffer->drawnFormat); + depalShader = depalShaderCache_->GetDepalettizeShader(clutFormat, framebuffer->drawnFormat); } - if (depal) { - // VulkanTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - // VulkanFBO *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, VK_FBO_8888); + if (depalShader) { + depalShaderCache_->SetPushBuffer(drawEngine_->GetPushBufferForTextureData()); + VulkanTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - //depalFBO->BeginPass(cmd); + Draw::Framebuffer *depalFBO = framebufferManager_->GetTempFBO( + framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); + draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); - struct Pos { - Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { - } - float x; - float y; - float z; - }; - struct UV { - UV(float u_, float v_) : u(u_), v(v_) { - } - float u; - float v; - }; - - Pos pos[4] = { - { -1, -1, -1 }, - { 1, -1, -1 }, - { 1, 1, -1 }, - { -1, 1, -1 }, - }; - UV uv[4] = { - { 0, 0 }, - { 1, 0 }, - { 1, 1 }, - { 0, 1 }, + Vulkan2D::Vertex verts[4] = { + { -1, -1, -1, 0, 0 }, + { 1, -1, -1, 1, 0 }, + { 1, 1, -1, 1, 1 }, + { -1, 1, -1, 0, 1 }, }; static const int indices[4] = { 0, 1, 3, 2 }; @@ -400,28 +389,32 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr const float top = v1 * invHalfHeight - 1.0f; const float bottom = v2 * invHalfHeight - 1.0f; // Points are: BL, BR, TR, TL. - pos[0] = Pos(left, bottom, -1.0f); - pos[1] = Pos(right, bottom, -1.0f); - pos[2] = Pos(right, top, -1.0f); - pos[3] = Pos(left, top, -1.0f); + verts[0].x = left; + verts[0].y = bottom; + verts[1].x = right; + verts[1].y = bottom; + verts[2].x = right; + verts[2].y = top; + verts[3].x = left; + verts[3].y = top; // And also the UVs, same order. const float uvleft = u1 * invWidth; const float uvright = u2 * invWidth; const float uvtop = v1 * invHeight; const float uvbottom = v2 * invHeight; - uv[0] = UV(uvleft, uvbottom); - uv[1] = UV(uvright, uvbottom); - uv[2] = UV(uvright, uvtop); - uv[3] = UV(uvleft, uvtop); + verts[0].u = uvleft; + verts[0].v = uvbottom; + verts[1].u = uvright; + verts[1].v = uvbottom; + verts[2].u = uvright; + verts[2].v = uvtop; + verts[3].u = uvleft; + verts[3].v = uvtop; } shaderManagerVulkan_->DirtyLastShader(); - //depalFBO->EndPass(cmd); - //depalFBO->TransitionToTexture(cmd); - //imageView = depalFBO->GetColorImageView(); - const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; @@ -429,7 +422,8 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL); gstate_c.SetTextureSimpleAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_SIMPLE); - // imageView_ = depalFbo->getImageView(). + draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0); + imageView_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); } else { entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE; diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 21beb9a18d..cae31787bb 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -61,6 +61,7 @@ private: DenseHashMap cache_; }; +class Vulkan2D; class TextureCacheVulkan : public TextureCacheCommon { public: @@ -83,6 +84,10 @@ public: void SetDrawEngine(DrawEngineVulkan *td) { drawEngine_ = td; } + void SetVulkan2D(Vulkan2D *vk2d); + void SetPushBuffer(VulkanPushBuffer *push) { + push_ = push; + } void ForgetLastTexture() override { lastBoundTexture = nullptr; @@ -119,6 +124,7 @@ private: VulkanContext *vulkan_; VulkanDeviceAllocator *allocator_; + VulkanPushBuffer *push_; SamplerCache samplerCache_; @@ -134,6 +140,7 @@ private: DepalShaderCacheVulkan *depalShaderCache_; ShaderManagerVulkan *shaderManagerVulkan_; DrawEngineVulkan *drawEngine_; + Vulkan2D *vulkan2D_; // Bound state to emulate an API similar to the others VkImageView imageView_ = VK_NULL_HANDLE; diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index caf06cbac8..af0240f736 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -36,6 +36,7 @@ void Vulkan2D::DestroyDeviceObjects() { for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { if (frameData_[i].descPool != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeleteDescriptorPool(frameData_[i].descPool); + frameData_[i].descPool = VK_NULL_HANDLE; } } for (auto it : pipelines_) { @@ -52,9 +53,16 @@ void Vulkan2D::DestroyDeviceObjects() { vkDestroyPipelineLayout(device, pipelineLayout_, nullptr); pipelineLayout_ = VK_NULL_HANDLE; } + + // pipelineBasicTex_ and pipelineBasicTex_ come from vulkan2D_. + if (pipelineCache_ != VK_NULL_HANDLE) { + vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_); + pipelineCache_ = VK_NULL_HANDLE; + } } void Vulkan2D::InitDeviceObjects() { + pipelineCache_ = vulkan_->CreatePipelineCache(); // All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated. VkDescriptorSetLayoutBinding bindings[2] = {}; bindings[0].descriptorCount = 1; @@ -188,7 +196,7 @@ VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, return desc; } -VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShaderModule vs, VkShaderModule fs) { +VkPipeline Vulkan2D::GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs) { PipelineKey key; key.vs = vs; key.fs = fs; @@ -298,7 +306,7 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade pipe.subpass = 0; VkPipeline pipeline; - VkResult result = vkCreateGraphicsPipelines(vulkan_->GetDevice(), cache, 1, &pipe, nullptr, &pipeline); + VkResult result = vkCreateGraphicsPipelines(vulkan_->GetDevice(), pipelineCache_, 1, &pipe, nullptr, &pipeline); if (result == VK_SUCCESS) { pipelines_[key] = pipeline; return pipeline; diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 3c32a63fac..ccbbfb9cb1 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -56,7 +56,7 @@ public: void DeviceRestore(VulkanContext *vulkan); void Shutdown(); - VkPipeline GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShaderModule vs, VkShaderModule fs); + VkPipeline GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs); VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } void BeginFrame(); void EndFrame(); @@ -72,9 +72,10 @@ private: void InitDeviceObjects(); void DestroyDeviceObjects(); - VulkanContext *vulkan_; - VkDescriptorSetLayout descriptorSetLayout_; - VkPipelineLayout pipelineLayout_; + VulkanContext *vulkan_ = nullptr; + VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE; + VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; // Yes, another one... struct DescriptorSetKey { From 3f503ca29730203ec58cc7dda671bc17b720c114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 30 Oct 2017 15:50:02 +0100 Subject: [PATCH 091/111] Implement the rest of Vulkan framebuffer depal. Not yet working though. --- Common/Vulkan/VulkanImage.cpp | 2 ++ GPU/Common/DepalettizeShaderCommon.cpp | 4 +-- GPU/Vulkan/DepalettizeShaderVulkan.cpp | 41 +++++++++++++++++++------- GPU/Vulkan/DepalettizeShaderVulkan.h | 1 - GPU/Vulkan/TextureCacheVulkan.cpp | 26 +++++++++++++--- GPU/Vulkan/TextureCacheVulkan.h | 6 ++-- ext/native/thin3d/thin3d_vulkan.cpp | 3 +- 7 files changed, 62 insertions(+), 21 deletions(-) diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp index 41cf32c824..0c0a5ff061 100644 --- a/Common/Vulkan/VulkanImage.cpp +++ b/Common/Vulkan/VulkanImage.cpp @@ -269,6 +269,8 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, 0, VK_ACCESS_TRANSFER_WRITE_BIT); break; default: + // If you planned to use UploadMip, you want VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. After the + // upload, you can transition. assert(false); break; } diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index b3d860870f..340d26ded3 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -44,7 +44,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang WRITE(p, "layout(set = 0, binding = 0) uniform sampler2D tex;\n"); WRITE(p, "layout(set = 0, binding = 1) uniform sampler2D pal;\n"); WRITE(p, "layout(location = 0) in vec2 v_texcoord0;\n"); - WRITE(p, "layout(location = 0) out vec4 fragColor0\n;"); + WRITE(p, "layout(location = 0) out vec4 fragColor0;\n"); } else { if (gl_extensions.IsGLES) { WRITE(p, "#version 300 es\n"); @@ -288,4 +288,4 @@ void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguag } } -#undef WRITE \ No newline at end of file +#undef WRITE diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index c785ee4372..e5814eafb2 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -22,6 +22,19 @@ #include "GPU/Vulkan/VulkanUtil.h" #include "Common/Vulkan/VulkanImage.h" +static const char depal_vs[] = R"(#version 400 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +layout (location = 0) in vec3 a_position; +layout (location = 1) in vec2 a_texcoord0; +layout (location = 0) out vec2 v_texcoord0; +out gl_PerVertex { vec4 gl_Position; }; +void main() { + v_texcoord0 = a_texcoord0; + gl_Position = vec4(a_position, 1.0); +} +)"; + static VkFormat GetClutDestFormat(GEPaletteFormat format) { switch (format) { case GE_CMODE_16BIT_ABGR4444: @@ -38,11 +51,14 @@ static VkFormat GetClutDestFormat(GEPaletteFormat format) { DepalShaderCacheVulkan::DepalShaderCacheVulkan(Draw::DrawContext *draw, VulkanContext *vulkan) : draw_(draw), vulkan_(vulkan) { - + std::string errors; + vshader_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, depal_vs, &errors); + assert(vshader_ != VK_NULL_HANDLE); } DepalShaderCacheVulkan::~DepalShaderCacheVulkan() { - + Clear(); + vulkan_->Delete().QueueDeleteShaderModule(vshader_); } DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(uint32_t clutMode, GEBufferFormat pixelFormat) { @@ -53,15 +69,16 @@ DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(uint32_t clutMod return shader->second; } - char *buffer = new char[2048]; - VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS); + char *buffer = new char[2048]; GenerateDepalShader(buffer, pixelFormat, GLSL_VULKAN); std::string error; VkShaderModule fshader = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, buffer, &error); if (fshader == VK_NULL_HANDLE) { + Crash(); + delete[] buffer; return nullptr; } @@ -74,10 +91,11 @@ DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(uint32_t clutMod depal->pipeline = pipeline; depal->code = buffer; cache_[id] = depal; - return nullptr; + return depal; } VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat, u32 clutID, u32 *rawClut) { + VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); const u32 realClutID = clutID ^ clutFormat; auto oldtex = texCache_.find(realClutID); @@ -93,9 +111,12 @@ VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat uint32_t pushOffset = push_->PushAligned(rawClut, 1024, 4, &pushBuffer); VulkanTexture *vktex = new VulkanTexture(vulkan_, alloc_); - vktex->CreateDirect(cmd_, texturePixels, 1, 1, destFormat, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT, nullptr); - vktex->UploadMip(cmd_, 0, texturePixels, 1, pushBuffer, pushOffset, texturePixels); - vktex->EndCreate(cmd_); + if (!vktex->CreateDirect(cmd, texturePixels, 1, 1, destFormat, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, nullptr)) { + Crash(); + } + vktex->UploadMip(cmd, 0, texturePixels, 1, pushBuffer, pushOffset, texturePixels); + vktex->EndCreate(cmd); DepalTextureVulkan *tex = new DepalTextureVulkan(); tex->texture = vktex; @@ -107,6 +128,8 @@ VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat void DepalShaderCacheVulkan::Clear() { for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) { // Delete the shader/pipeline too. + vulkan_->Delete().QueueDeletePipeline(shader->second->pipeline); + delete[] shader->second->code; delete shader->second; } cache_.clear(); @@ -116,8 +139,6 @@ void DepalShaderCacheVulkan::Clear() { delete tex->second; } texCache_.clear(); - - // TODO: Delete vertex shader. } void DepalShaderCacheVulkan::Decimate() { diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.h b/GPU/Vulkan/DepalettizeShaderVulkan.h index fb2c3db4a4..4762f07d2c 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.h +++ b/GPU/Vulkan/DepalettizeShaderVulkan.h @@ -66,7 +66,6 @@ public: private: u32 GenerateShaderID(uint32_t clutMode, GEBufferFormat pixelFormat); - VkCommandBuffer cmd_ = VK_NULL_HANDLE; Draw::DrawContext *draw_ = nullptr; VulkanContext *vulkan_ = nullptr; VulkanPushBuffer *push_ = nullptr; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index ce5124aaa1..7d9fb06710 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -23,6 +23,8 @@ #include "math/math_util.h" #include "profiler/profiler.h" #include "thin3d/thin3d.h" +#include "thin3d/VulkanRenderManager.h" + #include "Common/ColorConv.h" #include "Core/Config.h" #include "Core/Host.h" @@ -130,10 +132,19 @@ TextureCacheVulkan::TextureCacheVulkan(Draw::DrawContext *draw, VulkanContext *v lastBoundTexture = nullptr; allocator_ = new VulkanDeviceAllocator(vulkan_, TEXCACHE_MIN_SLAB_SIZE, TEXCACHE_MAX_SLAB_SIZE); + VkSamplerCreateInfo samp{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samp.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samp.magFilter = VK_FILTER_NEAREST; + samp.minFilter = VK_FILTER_NEAREST; + samp.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &samplerNearest_); SetupTextureDecoder(); } TextureCacheVulkan::~TextureCacheVulkan() { + vulkan_->Delete().QueueDeleteSampler(samplerNearest_); Clear(true); if (allocator_) { @@ -333,19 +344,19 @@ void TextureCacheVulkan::UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutB void TextureCacheVulkan::BindTexture(TexCacheEntry *entry) { if (!entry || !entry->vkTex) { imageView_ = VK_NULL_HANDLE; - sampler_ = VK_NULL_HANDLE; + curSampler_ = VK_NULL_HANDLE; return; } imageView_ = entry->vkTex->texture_->GetImageView(); SamplerCacheKey key; UpdateSamplingParams(*entry, key); - sampler_ = samplerCache_.GetOrCreateSampler(key); + curSampler_ = samplerCache_.GetOrCreateSampler(key); } void TextureCacheVulkan::Unbind() { imageView_ = VK_NULL_HANDLE; - sampler_ = VK_NULL_HANDLE; + curSampler_ = VK_NULL_HANDLE; InvalidateLastTexture(); } @@ -413,6 +424,13 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr verts[3].v = uvtop; } + VkBuffer pushed; + uint32_t offset = push_->PushAligned(verts, sizeof(verts), 4, &pushed); + VkImageView fbo = (VkImageView)draw_->GetFramebufferAPITexture(framebuffer->fbo, Draw::FB_COLOR_BIT, 0); + + VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(fbo, samplerNearest_, clutTexture->GetImageView(), samplerNearest_); + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, pushed, offset, 4); shaderManagerVulkan_->DirtyLastShader(); const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); @@ -435,7 +453,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr SamplerCacheKey samplerKey; SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, samplerKey); - sampler_ = samplerCache_.GetOrCreateSampler(samplerKey); + curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey); InvalidateLastTexture(entry); } diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index cae31787bb..1b168fd0f5 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -101,7 +101,7 @@ public: void GetVulkanHandles(VkImageView &imageView, VkSampler &sampler) { imageView = imageView_; - sampler = sampler_; + sampler = curSampler_; } void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, SamplerCacheKey &key); @@ -144,7 +144,9 @@ private: // Bound state to emulate an API similar to the others VkImageView imageView_ = VK_NULL_HANDLE; - VkSampler sampler_ = VK_NULL_HANDLE; + VkSampler curSampler_ = VK_NULL_HANDLE; + + VkSampler samplerNearest_ = VK_NULL_HANDLE; }; VkFormat getClutDestFormatVulkan(GEPaletteFormat format); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index b031b89e76..3a05ae2813 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1140,7 +1140,6 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); } -// TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing. void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) { int mask = 0; if (clearMask & FBChannel::FB_COLOR_BIT) @@ -1359,9 +1358,9 @@ void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void renderManager_.CreateBackbuffers(); break; default: + assert(false); break; } - // Noop } } // namespace Draw From 07dfda0633b8bddb044f0a8fd387ac9d0c010c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 31 Oct 2017 10:54:01 +0100 Subject: [PATCH 092/111] Vulkan depal code now passes validation, but produces black. --- GPU/Vulkan/DrawEngineVulkan.cpp | 1 - GPU/Vulkan/TextureCacheVulkan.cpp | 11 ++++++++++- Windows/GPU/WindowsVulkanContext.cpp | 5 +++++ ext/native/thin3d/VulkanRenderManager.cpp | 1 + ext/native/thin3d/thin3d_vulkan.cpp | 17 +++++++++++++++++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 537dfdc756..42f44621d2 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -897,7 +897,6 @@ void DrawEngineVulkan::DoFlush() { UpdateUBOs(frame); VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf); - { PROFILE_THIS_SCOPE("renderman_q"); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 7d9fb06710..df5b32e4a5 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -426,10 +426,14 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr VkBuffer pushed; uint32_t offset = push_->PushAligned(verts, sizeof(verts), 4, &pushed); - VkImageView fbo = (VkImageView)draw_->GetFramebufferAPITexture(framebuffer->fbo, Draw::FB_COLOR_BIT, 0); + + ILOG("DEPAL 2"); + draw_->BindFramebufferAsTexture(framebuffer->fbo, 0, Draw::FB_COLOR_BIT, 0); + VkImageView fbo = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(fbo, samplerNearest_, clutTexture->GetImageView(), samplerNearest_); VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + renderManager->BindPipeline(depalShader->pipeline); renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, pushed, offset, 4); shaderManagerVulkan_->DirtyLastShader(); @@ -440,11 +444,16 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL); gstate_c.SetTextureSimpleAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_SIMPLE); + framebufferManager_->RebindFramebuffer(); draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0); imageView_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); + + // Need to rebind the pipeline since we switched it. + drawEngine_->DirtyPipeline(); } else { entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE; + framebufferManager_->RebindFramebuffer(); imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650); diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 2c4a8992f4..e336b5e9a5 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -128,6 +128,11 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep return false; if (msgCode == 64) // Another useless perf warning that will be seen less and less as we optimize - vkCmdClearAttachments() issued on command buffer object 0x00000195296C6D40 prior to any Draw Cmds. It is recommended you use RenderPass LOAD_OP_CLEAR on Attachments prior to any Draw. return false; + + // TEMPORARy + if (msgCode == 6) + return false; + #ifdef _WIN32 std::string msg = message.str(); OutputDebugStringA(msg.c_str()); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 6662f5ca6d..943cb53dd4 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -306,6 +306,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR if (steps_.size() && steps_.back()->stepType == VKRStepType::RENDER && steps_.back()->render.framebuffer == fb) { if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR) { // We don't move to a new step, this bind was unnecessary. + ILOG("Redundant bind"); return; } } diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 3a05ae2813..6e55732cd7 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -343,6 +343,8 @@ private: DataFormat format_; }; +class VKFramebuffer; + class VKContext : public DrawContext { public: VKContext(VulkanContext *vulkan); @@ -481,6 +483,7 @@ private: VkDescriptorSetLayout descriptorSetLayout_; VkPipelineLayout pipelineLayout_; VkPipelineCache pipelineCache_; + VKFramebuffer *curFramebuffer_ = nullptr; VkDevice device_; VkQueue queue_; @@ -1303,13 +1306,27 @@ void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPass VKFramebuffer *fb = (VKFramebuffer *)fbo; VKRRenderPassAction color = (VKRRenderPassAction)rp.color; // same values. VKRRenderPassAction depth = (VKRRenderPassAction)rp.color; // same values. + + if (fb) { + ILOG("Binding image as RT: %x (clear_color: %d)", (int)fb->GetFB()->color.image, rp.color == RPAction::CLEAR); + } + + if (fb && boundImageView_[0] == fb->GetFB()->color.imageView) { + // Crash(); + } + renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); + curFramebuffer_ = fb; } // color must be 0, for now. void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) { VKFramebuffer *fb = (VKFramebuffer *)fbo; + if (fb == curFramebuffer_) { + Crash(); + } + int aspect = 0; if (channelBit & FBChannel::FB_COLOR_BIT) aspect |= VK_IMAGE_ASPECT_COLOR_BIT; if (channelBit & FBChannel::FB_DEPTH_BIT) aspect |= VK_IMAGE_ASPECT_DEPTH_BIT; From ed2731d1978455296258a8a91befb0fcbdac0cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 31 Oct 2017 12:02:10 +0100 Subject: [PATCH 093/111] Vulkan: Fix depal and shader blending. --- GPU/Vulkan/DepalettizeShaderVulkan.cpp | 4 +-- GPU/Vulkan/DepalettizeShaderVulkan.h | 4 +-- GPU/Vulkan/DrawEngineVulkan.cpp | 25 ++++++++++++---- GPU/Vulkan/DrawEngineVulkan.h | 11 ++++--- GPU/Vulkan/FramebufferVulkan.cpp | 10 ++----- GPU/Vulkan/StateMappingVulkan.cpp | 12 +++----- GPU/Vulkan/TextureCacheVulkan.cpp | 24 +++++++-------- GPU/Vulkan/VulkanUtil.h | 1 + ext/native/thin3d/VulkanRenderManager.cpp | 16 +++++++--- ext/native/thin3d/VulkanRenderManager.h | 2 +- ext/native/thin3d/thin3d.h | 3 +- ext/native/thin3d/thin3d_vulkan.cpp | 36 ++++++++--------------- 12 files changed, 76 insertions(+), 72 deletions(-) diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index e5814eafb2..322e0e292c 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -127,9 +127,7 @@ VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat void DepalShaderCacheVulkan::Clear() { for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) { - // Delete the shader/pipeline too. - vulkan_->Delete().QueueDeletePipeline(shader->second->pipeline); - delete[] shader->second->code; + // Don't need to destroy the pipelines, they're handled by Vulkan2D. delete shader->second; } cache_.clear(); diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.h b/GPU/Vulkan/DepalettizeShaderVulkan.h index 4762f07d2c..4e6e119b84 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.h +++ b/GPU/Vulkan/DepalettizeShaderVulkan.h @@ -32,13 +32,13 @@ public: delete[] code; } // A Vulkan2D pipeline. Set texture to slot 0 and palette texture to slot 1. - VkPipeline pipeline; + VkPipeline pipeline = VK_NULL_HANDLE; const char *code = nullptr;; }; class DepalTextureVulkan { public: - VulkanTexture *texture; + VulkanTexture *texture = nullptr; int lastFrame; }; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 42f44621d2..949d215c5a 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -202,7 +202,7 @@ void DrawEngineVulkan::InitDeviceObjects() { samp.flags = 0; samp.magFilter = VK_FILTER_NEAREST; samp.minFilter = VK_FILTER_NEAREST; - res = vkCreateSampler(device, &samp, nullptr, &depalSampler_); + res = vkCreateSampler(device, &samp, nullptr, &samplerSecondary_); res = vkCreateSampler(device, &samp, nullptr, &nullSampler_); assert(VK_SUCCESS == res); @@ -245,8 +245,8 @@ void DrawEngineVulkan::DestroyDeviceObjects() { for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].Destroy(vulkan_); } - if (depalSampler_ != VK_NULL_HANDLE) - vulkan_->Delete().QueueDeleteSampler(depalSampler_); + if (samplerSecondary_ != VK_NULL_HANDLE) + vulkan_->Delete().QueueDeleteSampler(samplerSecondary_); if (nullSampler_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteSampler(nullSampler_); if (pipelineLayout_ != VK_NULL_HANDLE) @@ -507,12 +507,11 @@ void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, } } - VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone) { DescriptorSetKey key; key.imageView_ = imageView; key.sampler_ = sampler; - key.secondaryImageView_ = VK_NULL_HANDLE; + key.secondaryImageView_ = boundSecondary_; key.base_ = base; key.light_ = light; key.bone_ = bone; @@ -561,6 +560,21 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView n++; } + if (boundSecondary_) { + // TODO: Also support LAYOUT_GENERAL to be able to texture from framebuffers without transitioning them? + tex.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + tex.imageView = boundSecondary_; + tex.sampler = samplerSecondary_; + writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[n].pNext = nullptr; + writes[n].dstBinding = DRAW_BINDING_2ND_TEXTURE; + writes[n].pImageInfo = &tex; + writes[n].descriptorCount = 1; + writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[n].dstSet = desc; + n++; + } + // Skipping 2nd texture for now. // Tessellation data textures @@ -903,7 +917,6 @@ void DrawEngineVulkan::DoFlush() { const uint32_t dynamicUBOOffsets[3] = { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; - // vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets); int stride = dec_->GetDecVtxFmt().stride; diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 5ecd84ad61..e1ebba790f 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -179,6 +179,7 @@ private: struct FrameData; void ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); + void ResetShaderBlending(); void InitDeviceObjects(); void DestroyDeviceObjects(); @@ -200,6 +201,10 @@ private: VulkanPipeline *lastPipeline_; VkDescriptorSet lastDs_ = VK_NULL_HANDLE; + // Secondary texture for shader blending + VkImageView boundSecondary_ = VK_NULL_HANDLE; + VkSampler samplerSecondary_ = VK_NULL_HANDLE; + PrehashMap vai_; VulkanPushBuffer *vertexCache_; int decimationCounter_ = 0; @@ -237,16 +242,14 @@ private: TextureCacheVulkan *textureCache_ = nullptr; FramebufferManagerVulkan *framebufferManager_ = nullptr; - VkSampler depalSampler_; - // State cache uint64_t dirtyUniforms_; uint32_t baseUBOOffset; uint32_t lightUBOOffset; uint32_t boneUBOOffset; VkBuffer baseBuf, lightBuf, boneBuf; - VkImageView imageView; - VkSampler sampler; + VkImageView imageView = VK_NULL_HANDLE; + VkSampler sampler = VK_NULL_HANDLE; // Null texture VulkanTexture *nullTexture_ = nullptr; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 4bf271f7e6..9580f2f7ee 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -337,7 +337,7 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); + VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); if ((flags & DRAWTEX_KEEP_TEX) == 0) overrideImageView_ = VK_NULL_HANDLE; VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); @@ -438,10 +438,6 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V } // Currently rendering to this framebuffer. Need to make a copy. if (!skipCopy && framebuffer == currentRenderVfb_) { - // ignore this case for now, doesn't work - // ILOG("Texturing from current render Vfb!"); - return VK_NULL_HANDLE; - // TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size. Draw::Framebuffer *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); if (renderCopy) { @@ -453,10 +449,10 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V } else { draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0); } - return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); + return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); } else if (framebuffer != currentRenderVfb_) { draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0); - return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); + return (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); } else { ERROR_LOG_REPORT_ONCE(vulkanSelfTexture, G3D, "Attempting to texture from target"); // To do this safely in Vulkan, we need to use input attachments. diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 3c170cc784..c1d955fc99 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -26,14 +26,12 @@ #include "Core/System.h" #include "Core/Config.h" #include "Core/Reporting.h" -//#include "GPU/Vulkan/StateMappingVulkan.h" #include "GPU/Vulkan/GPU_Vulkan.h" #include "GPU/Vulkan/PipelineManagerVulkan.h" #include "GPU/Vulkan/TextureCacheVulkan.h" #include "GPU/Vulkan/FramebufferVulkan.h" #include "GPU/Vulkan/ShaderManagerVulkan.h" #include "GPU/Vulkan/DrawEngineVulkan.h" -//#include "GPU/Vulkan/PixelShaderGeneratorVulkan.h" // These tables all fit into u8s. static const VkBlendFactor vkBlendFactorLookup[(size_t)BlendFactor::COUNT] = { @@ -123,8 +121,8 @@ static const VkLogicOp logicOps[] = { VK_LOGIC_OP_SET, }; -void ResetShaderBlending() { - // +void DrawEngineVulkan::ResetShaderBlending() { + boundSecondary_ = VK_NULL_HANDLE; } // TODO: Do this more progressively. No need to compute the entire state if the entire state hasn't changed. @@ -136,8 +134,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) { - // Unfortunately, this isn't implemented yet. - gstate_c.SetAllowShaderBlend(false); + gstate_c.SetAllowShaderBlend(!g_Config.bDisableSlowFramebufEffects); if (gstate.isModeClear()) { key.logicOpEnable = false; key.logicOp = VK_LOGIC_OP_CLEAR; @@ -375,15 +372,14 @@ void DrawEngineVulkan::ApplyDrawStateLate(VulkanRenderManager *renderManager, bo // TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? if (!gstate.isModeClear()) { // TODO: Test texture? - /* if (fboTexNeedBind_) { // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); // If we are rendering at a higher resolution, linear is probably best for the dest color. + boundSecondary_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE1_IMAGEVIEW); fboTexBound_ = true; fboTexNeedBind_ = false; } - */ } if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) { diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index df5b32e4a5..5fc95714ce 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -375,12 +375,11 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); Vulkan2D::Vertex verts[4] = { - { -1, -1, -1, 0, 0 }, - { 1, -1, -1, 1, 0 }, - { 1, 1, -1, 1, 1 }, - { -1, 1, -1, 0, 1 }, + { -1, -1, 0.0f, 0, 0 }, + { 1, -1, 0.0f, 1, 0 }, + { -1, 1, 0.0f, 0, 1 }, + { 1, 1, 0.0f, 1, 1 }, }; - static const int indices[4] = { 0, 1, 3, 2 }; // If min is not < max, then we don't have values (wasn't set during decode.) if (gstate_c.vertBounds.minV < gstate_c.vertBounds.maxV) { @@ -404,9 +403,9 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr verts[0].y = bottom; verts[1].x = right; verts[1].y = bottom; - verts[2].x = right; + verts[2].x = left; verts[2].y = top; - verts[3].x = left; + verts[3].x = right; verts[3].y = top; // And also the UVs, same order. @@ -418,22 +417,23 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr verts[0].v = uvbottom; verts[1].u = uvright; verts[1].v = uvbottom; - verts[2].u = uvright; + verts[2].u = uvleft; verts[2].v = uvtop; - verts[3].u = uvleft; + verts[3].u = uvright; verts[3].v = uvtop; } VkBuffer pushed; uint32_t offset = push_->PushAligned(verts, sizeof(verts), 4, &pushed); - ILOG("DEPAL 2"); draw_->BindFramebufferAsTexture(framebuffer->fbo, 0, Draw::FB_COLOR_BIT, 0); - VkImageView fbo = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); + VkImageView fbo = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(fbo, samplerNearest_, clutTexture->GetImageView(), samplerNearest_); VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); renderManager->BindPipeline(depalShader->pipeline); + renderManager->SetScissor(VkRect2D{ {0, 0}, { framebuffer->renderWidth, framebuffer->renderHeight} }); + renderManager->SetViewport(VkViewport{ 0.f, 0.f, (float)framebuffer->renderWidth, (float)framebuffer->renderHeight, 0.f, 1.f }); renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, pushed, offset, 4); shaderManagerVulkan_->DirtyLastShader(); @@ -446,7 +446,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr framebufferManager_->RebindFramebuffer(); draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0); - imageView_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW); + imageView_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); // Need to rebind the pipeline since we switched it. drawEngine_->DirtyPipeline(); diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index ccbbfb9cb1..e9696a7a9c 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -56,6 +56,7 @@ public: void DeviceRestore(VulkanContext *vulkan); void Shutdown(); + // The only supported primitive is the triangle strip, for simplicity. VkPipeline GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs); VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } void BeginFrame(); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 943cb53dd4..d6dbd62930 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -303,13 +303,22 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { // Eliminate dupes. - if (steps_.size() && steps_.back()->stepType == VKRStepType::RENDER && steps_.back()->render.framebuffer == fb) { + if (steps_.size() && steps_.back()->render.framebuffer == fb && steps_.back()->stepType == VKRStepType::RENDER) { if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR) { - // We don't move to a new step, this bind was unnecessary. - ILOG("Redundant bind"); + // We don't move to a new step, this bind was unnecessary and we can safely skip it. return; } } + if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color == VKRRenderPassAction::KEEP && curRenderStep_->render.depthStencil == VKRRenderPassAction::KEEP) { + // Can trivially kill the last empty render step. + assert(steps_.back() == curRenderStep_); + delete steps_.back(); + steps_.pop_back(); + curRenderStep_ = nullptr; + } + if (curRenderStep_ && curRenderStep_->commands.size() == 0) { + ILOG("Empty render step. Usually happens after uploading pixels.."); + } VKRStep *step = new VKRStep{ VKRStepType::RENDER }; // This is what queues up new passes, and can end previous ones. @@ -656,7 +665,6 @@ void VulkanRenderManager::Run(int frame) { EndSubmitFrame(frame); - VLOG("PULL: Finished running frame %d", frame); } diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 717539cee0..a9d94f05fa 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -226,7 +226,7 @@ private: int curWidth_; int curHeight_; bool insideFrame_ = false; - VKRStep *curRenderStep_; + VKRStep *curRenderStep_ = nullptr; std::vector steps_; // Execution time state diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 0dead1552a..eb316e66e8 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -264,7 +264,8 @@ enum class NativeObject { BACKBUFFER_RENDERPASS, FRAMEBUFFER_RENDERPASS, INIT_COMMANDBUFFER, - BOUND_TEXTURE_IMAGEVIEW, + BOUND_TEXTURE0_IMAGEVIEW, + BOUND_TEXTURE1_IMAGEVIEW, RENDER_MANAGER, }; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 6e55732cd7..06d87cfb1e 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -456,9 +456,10 @@ public: return (uintptr_t)renderManager_.GetCompatibleRenderPass(); case NativeObject::INIT_COMMANDBUFFER: return (uintptr_t)renderManager_.GetInitCmd(); - case NativeObject::BOUND_TEXTURE_IMAGEVIEW: + case NativeObject::BOUND_TEXTURE0_IMAGEVIEW: return (uintptr_t)boundImageView_[0]; - + case NativeObject::BOUND_TEXTURE1_IMAGEVIEW: + return (uintptr_t)boundImageView_[1]; case NativeObject::RENDER_MANAGER: return (uintptr_t)&renderManager_; default: @@ -480,9 +481,9 @@ private: VKBuffer *curIBuffer_ = nullptr; int curIBufferOffset_ = 0; - VkDescriptorSetLayout descriptorSetLayout_; - VkPipelineLayout pipelineLayout_; - VkPipelineCache pipelineCache_; + VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE; + VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; VKFramebuffer *curFramebuffer_ = nullptr; VkDevice device_; @@ -490,12 +491,12 @@ private: int queueFamilyIndex_; enum { - MAX_BOUND_TEXTURES = 1, + MAX_BOUND_TEXTURES = 2, MAX_FRAME_COMMAND_BUFFERS = 256, }; - VKTexture *boundTextures_[MAX_BOUND_TEXTURES]; - VKSamplerState *boundSamplers_[MAX_BOUND_TEXTURES]; - VkImageView boundImageView_[MAX_BOUND_TEXTURES]; + VKTexture *boundTextures_[MAX_BOUND_TEXTURES]{}; + VKSamplerState *boundSamplers_[MAX_BOUND_TEXTURES]{}; + VkImageView boundImageView_[MAX_BOUND_TEXTURES]{}; struct FrameData { VulkanPushBuffer *pushBuffer; @@ -1291,9 +1292,7 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, VKFramebuffer *src = (VKFramebuffer *)srcfb; int aspectMask = 0; - if (channelBits & FBChannel::FB_COLOR_BIT) { - aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; - } + if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; @@ -1301,20 +1300,11 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, return true; } - void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { VKFramebuffer *fb = (VKFramebuffer *)fbo; VKRRenderPassAction color = (VKRRenderPassAction)rp.color; // same values. VKRRenderPassAction depth = (VKRRenderPassAction)rp.color; // same values. - if (fb) { - ILOG("Binding image as RT: %x (clear_color: %d)", (int)fb->GetFB()->color.image, rp.color == RPAction::CLEAR); - } - - if (fb && boundImageView_[0] == fb->GetFB()->color.imageView) { - // Crash(); - } - renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); curFramebuffer_ = fb; } @@ -1332,9 +1322,7 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne if (channelBit & FBChannel::FB_DEPTH_BIT) aspect |= VK_IMAGE_ASPECT_DEPTH_BIT; if (channelBit & FBChannel::FB_STENCIL_BIT) aspect |= VK_IMAGE_ASPECT_STENCIL_BIT; - ILOG("Binding image as texture: %x", (int)fb->GetFB()->color.image); - - boundImageView_[0] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); + boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, attachment); } uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) { From bbfc32f131969b5379c45d9c52fd68610bc73b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 31 Oct 2017 12:57:27 +0100 Subject: [PATCH 094/111] Remove disabling of validation warning. It works. --- Windows/GPU/WindowsVulkanContext.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index e336b5e9a5..21dcb026ea 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -129,10 +129,6 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep if (msgCode == 64) // Another useless perf warning that will be seen less and less as we optimize - vkCmdClearAttachments() issued on command buffer object 0x00000195296C6D40 prior to any Draw Cmds. It is recommended you use RenderPass LOAD_OP_CLEAR on Attachments prior to any Draw. return false; - // TEMPORARy - if (msgCode == 6) - return false; - #ifdef _WIN32 std::string msg = message.str(); OutputDebugStringA(msg.c_str()); From 9e734b3791935e8796d219acc17b436a4eb20b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 01:25:24 +0100 Subject: [PATCH 095/111] Decimate descriptors every frame until I find a better solution. Fixes flicker in videos in several games. --- GPU/Vulkan/DrawEngineVulkan.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 949d215c5a..a71ddf4da5 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -57,7 +57,7 @@ enum { }; #define VERTEXCACHE_DECIMATION_INTERVAL 17 -#define DESCRIPTORSET_DECIMATION_INTERVAL 13 +#define DESCRIPTORSET_DECIMATION_INTERVAL 1 // Temporarily cut to 1. Handle reuse breaks this when textures get deleted. enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 }; @@ -295,6 +295,7 @@ void DrawEngineVulkan::BeginFrame() { // TODO : Find a better place to do this. if (!nullTexture_) { + ILOG("INIT : Creating null texture"); VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); nullTexture_ = new VulkanTexture(vulkan_); int w = 8; @@ -1087,6 +1088,7 @@ void DrawEngineVulkan::UpdateUBOs(FrameData *frame) { void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) { int rowPitch; + ILOG("INIT : Prep tess"); VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); // Position From 74861d2d73aeb4fe733c67154eab306cceea93ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 31 Oct 2017 21:15:19 +0100 Subject: [PATCH 096/111] Vulkan: Make backbuffer transitions part of backbuffer render pass. Optimize depth buffer memory operations. --- Common/Vulkan/VulkanContext.cpp | 14 -------------- Common/Vulkan/VulkanContext.h | 3 --- ext/native/thin3d/VulkanQueueRunner.cpp | 8 ++++---- ext/native/thin3d/VulkanRenderManager.cpp | 7 +------ 4 files changed, 5 insertions(+), 27 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index a49cd58824..3fcfcc49cb 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -153,20 +153,6 @@ VulkanContext::~VulkanContext() { VulkanFree(); } -void TransitionToPresent(VkCommandBuffer cmd, VkImage image) { - TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT); -} - -void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) { - TransitionImageLayout2(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); -} - void VulkanContext::BeginFrame() { FrameData *frame = &frame_[curFrame_]; // Process pending deletes. diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index e8204dba4c..51c46409c9 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -393,9 +393,6 @@ void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, VkImageAspectFla VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); -void TransitionFromPresent(VkCommandBuffer cmd, VkImage image); -void TransitionToPresent(VkCommandBuffer cmd, VkImage image); - // GLSL compiler void init_glslang(); void finalize_glslang(); diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index f4028a0bfa..d8b358ea79 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -54,16 +54,16 @@ void VulkanQueueRunner::InitBackbufferRenderPass() { attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // We don't want to preserve the backbuffer between frames so we really don't care. + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // We only render once to the backbuffer per frame so we can do this here. attachments[0].flags = 0; attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; // must use this same format later for the back depth buffer. attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // Don't care about storing backbuffer Z - we clear it anyway. attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachments[1].flags = 0; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index d6dbd62930..2bcee4cb28 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -302,6 +302,7 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { } void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { + assert(insideFrame_); // Eliminate dupes. if (steps_.size() && steps_.back()->render.framebuffer == fb && steps_.back()->stepType == VKRStepType::RENDER) { if (color != VKRRenderPassAction::CLEAR && depth != VKRRenderPassAction::CLEAR) { @@ -570,9 +571,6 @@ void VulkanRenderManager::BeginSubmitFrame(int frame) { assert(res == VK_SUCCESS); - // TODO: Is it best to do this here, or combine with some other transition, or just do it right before the backbuffer bind-for-render? - TransitionFromPresent(frameData.mainCmd, swapchainImages_[frameData.curSwapchainImage].image); - queueRunner_.SetBackbuffer(framebuffers_[frameData.curSwapchainImage]); frameData.hasBegun = true; @@ -629,8 +627,6 @@ void VulkanRenderManager::EndSubmitFrame(int frame) { frameData.hasBegun = false; insideFrame_ = false; - TransitionToPresent(frameData.mainCmd, swapchainImages_[frameData.curSwapchainImage].image); - Submit(frame, true); VkSwapchainKHR swapchain = vulkan_->GetSwapchain(); @@ -640,7 +636,6 @@ void VulkanRenderManager::EndSubmitFrame(int frame) { present.pImageIndices = &frameData.curSwapchainImage; present.pWaitSemaphores = &renderingCompleteSemaphore_; present.waitSemaphoreCount = 1; - present.pResults = nullptr; VkResult res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI From 330bdb8be8a1eb5e490f7bc77345892d99745200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 31 Oct 2017 23:48:17 +0100 Subject: [PATCH 097/111] Add and update some comments. --- Common/Vulkan/VulkanContext.h | 2 ++ Common/Vulkan/VulkanImage.h | 1 + Core/Core.vcxproj | 5 ++++- GPU/D3D11/TextureCacheD3D11.cpp | 5 ++--- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 51c46409c9..f647f4d0f2 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -306,6 +306,8 @@ public: return swapchainFormat_; } + // 1 for no frame overlap and thus minimal latency but worst performance. + // 2 is an OK compromise, while 3 performs best but risks slightly higher latency. enum { MAX_INFLIGHT_FRAMES = 3, }; diff --git a/Common/Vulkan/VulkanImage.h b/Common/Vulkan/VulkanImage.h index 51ffbe5e22..7a6bff62f6 100644 --- a/Common/Vulkan/VulkanImage.h +++ b/Common/Vulkan/VulkanImage.h @@ -35,6 +35,7 @@ public: void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels void EndCreate(VkCommandBuffer cmd); + // Dangerous. void TransitionForUpload(VkCommandBuffer cmd); int GetNumMips() const { return numMips_; } diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index dda064edaa..cdb0667fc8 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -236,7 +236,10 @@ - + + MaxSpeed + Default + diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 789f3e4450..26744c22b8 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -514,9 +514,6 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry, bool replaceIma } } - // If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient. - DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); - int scaleFactor = standardScaleFactor_; // Rachet down scale factor in low-memory mode. @@ -573,6 +570,8 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry, bool replaceIma maxLevel = 0; } + DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); + if (IsFakeMipmapChange()) { // NOTE: Since the level is not part of the cache key, we assume it never changes. u8 level = std::max(0, gstate.getTexLevelOffset16() / 16); From 90d4296a6ad3ebff8a2a84456e45cb567c88db65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 31 Oct 2017 23:49:13 +0100 Subject: [PATCH 098/111] Add a QueueRunner logging facility. Set up a subpass dependency for the backbuffer pass. --- GPU/Vulkan/FramebufferVulkan.cpp | 1 + ext/native/thin3d/VulkanQueueRunner.cpp | 99 ++++++++++++++++++++--- ext/native/thin3d/VulkanQueueRunner.h | 6 ++ ext/native/thin3d/VulkanRenderManager.cpp | 14 +++- 4 files changed, 105 insertions(+), 15 deletions(-) diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 9580f2f7ee..d1bf945bd0 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -225,6 +225,7 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor // TODO: We can just change the texture format and flip some bits around instead of this. // Could share code with the texture cache perhaps. + // Could also convert directly into the pushbuffer easily. const uint8_t *data = srcPixels; if (srcPixelFormat != GE_FORMAT_8888 || srcStride != width) { u32 neededSize = width * height * 4; diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index d8b358ea79..8a7b4ccafd 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -68,7 +68,7 @@ void VulkanQueueRunner::InitBackbufferRenderPass() { attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachments[1].flags = 0; - VkAttachmentReference color_reference = {}; + VkAttachmentReference color_reference{}; color_reference.attachment = 0; color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -76,7 +76,7 @@ void VulkanQueueRunner::InitBackbufferRenderPass() { depth_reference.attachment = 1; depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; + VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.flags = 0; subpass.inputAttachmentCount = 0; @@ -88,14 +88,23 @@ void VulkanQueueRunner::InitBackbufferRenderPass() { subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = nullptr; - VkRenderPassCreateInfo rp_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - rp_info.pNext = nullptr; + // For the built-in layout transitions. + VkSubpassDependency dep{}; + dep.srcSubpass = VK_SUBPASS_EXTERNAL; + dep.dstSubpass = 0; + dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dep.srcAccessMask = 0; + dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; rp_info.attachmentCount = 2; rp_info.pAttachments = attachments; rp_info.subpassCount = 1; rp_info.pSubpasses = &subpass; - rp_info.dependencyCount = 0; - rp_info.pDependencies = nullptr; + rp_info.dependencyCount = 1; + rp_info.pDependencies = &dep; res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, nullptr, &backbufferRenderPass_); assert(res == VK_SUCCESS); @@ -112,7 +121,7 @@ void VulkanQueueRunner::InitRenderpasses() { attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // TODO: Look into auto-transitioning to SAMPLED when appropriate. attachments[0].flags = 0; attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; @@ -125,15 +134,15 @@ void VulkanQueueRunner::InitRenderpasses() { attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachments[1].flags = 0; - VkAttachmentReference color_reference = {}; + VkAttachmentReference color_reference{}; color_reference.attachment = 0; color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkAttachmentReference depth_reference = {}; + VkAttachmentReference depth_reference{}; depth_reference.attachment = 1; depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; + VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.flags = 0; subpass.inputAttachmentCount = 0; @@ -145,13 +154,12 @@ void VulkanQueueRunner::InitRenderpasses() { subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = nullptr; - VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; rp.attachmentCount = 2; rp.pAttachments = attachments; rp.subpassCount = 1; rp.pSubpasses = &subpass; rp.dependencyCount = 0; - rp.pDependencies = nullptr; for (int depth = 0; depth < 3; depth++) { switch ((VKRRenderPassAction)depth) { @@ -201,6 +209,73 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector &steps) { + ILOG("======================================="); + for (int i = 0; i < steps.size(); i++) { + const VKRStep &step = *steps[i]; + switch (step.stepType) { + case VKRStepType::RENDER: + LogRenderPass(step); + break; + case VKRStepType::COPY: + LogCopy(step); + break; + case VKRStepType::BLIT: + LogBlit(step); + break; + case VKRStepType::READBACK: + LogReadback(step); + break; + } + } +} + +void VulkanQueueRunner::LogRenderPass(const VKRStep &pass) { + int fb = (int)(intptr_t)(pass.render.framebuffer ? pass.render.framebuffer->framebuf : 0); + ILOG("RenderPass Begin(%x)", fb); + for (auto &cmd : pass.commands) { + switch (cmd.cmd) { + case VKRRenderCommand::BIND_PIPELINE: + ILOG(" BindPipeline(%x)", (int)(intptr_t)cmd.pipeline.pipeline); + break; + case VKRRenderCommand::BLEND: + ILOG(" Blend(%f, %f, %f, %f)", cmd.blendColor.color[0], cmd.blendColor.color[1], cmd.blendColor.color[2], cmd.blendColor.color[3]); + break; + case VKRRenderCommand::CLEAR: + ILOG(" Clear"); + break; + case VKRRenderCommand::DRAW: + ILOG(" Draw(%d)", cmd.draw.count); + break; + case VKRRenderCommand::DRAW_INDEXED: + ILOG(" DrawIndexed(%d)", cmd.drawIndexed.count); + break; + case VKRRenderCommand::SCISSOR: + ILOG(" Scissor(%d, %d, %d, %d)", (int)cmd.scissor.scissor.offset.x, (int)cmd.scissor.scissor.offset.y, (int)cmd.scissor.scissor.extent.width, (int)cmd.scissor.scissor.extent.height); + break; + case VKRRenderCommand::STENCIL: + ILOG(" Stencil(ref=%d, compare=%d, write=%d)", cmd.stencil.stencilRef, cmd.stencil.stencilCompareMask, cmd.stencil.stencilWriteMask); + break; + case VKRRenderCommand::VIEWPORT: + ILOG(" Viewport(%f, %f, %f, %f, %f, %f)", cmd.viewport.vp.x, cmd.viewport.vp.y, cmd.viewport.vp.width, cmd.viewport.vp.height, cmd.viewport.vp.minDepth, cmd.viewport.vp.maxDepth); + break; + } + } + ILOG("RenderPass End(%x)", fb); +} + +void VulkanQueueRunner::LogCopy(const VKRStep &pass) { + ILOG("Copy()"); +} + +void VulkanQueueRunner::LogBlit(const VKRStep &pass) { + ILOG("Blit()"); +} + +void VulkanQueueRunner::LogReadback(const VKRStep &pass) { + ILOG("Readback"); +} + void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) { // TODO: If there are multiple, we can transition them together. for (const auto &iter : step.preTransitions) { diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 05accdf6a5..a35a664f35 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -141,6 +141,7 @@ public: backbuffer_ = fb; } void RunSteps(VkCommandBuffer cmd, const std::vector &steps); + void LogSteps(const std::vector &steps); void CreateDeviceObjects(); void DestroyDeviceObjects(); @@ -168,6 +169,11 @@ private: void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd); void PerformReadback(const VKRStep &pass, VkCommandBuffer cmd); + void LogRenderPass(const VKRStep &pass); + void LogCopy(const VKRStep &pass); + void LogBlit(const VKRStep &pass); + void LogReadback(const VKRStep &pass); + static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 2bcee4cb28..58ed64bbc1 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -597,13 +597,13 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { cmdBufs.push_back(frameData.mainCmd); - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; if (triggerFence) { submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &acquireSemaphore_; } - VkPipelineStageFlags waitStage[1] = { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; + VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; submit_info.pWaitDstStageMask = waitStage; submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); submit_info.pCommandBuffers = cmdBufs.data(); @@ -612,7 +612,12 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; } res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, triggerFence ? frameData.fence : VK_NULL_HANDLE); - assert(res == VK_SUCCESS); + if (res == VK_ERROR_DEVICE_LOST) { + // _assert_msg_(G3D, false, "Lost the Vulkan device! What did we do wrong?"); + ELOG("DEVICE LOST"); + } else { + assert(res == VK_SUCCESS); + } if (useThread) { VLOG("PULL: Frame %d.readyForFence = true", frame); @@ -655,6 +660,7 @@ void VulkanRenderManager::Run(int frame) { FrameData &frameData = frameData_[frame]; auto &stepsOnThread = frameData_[frame].steps; VkCommandBuffer cmd = frameData.mainCmd; + // queueRunner_.LogSteps(stepsOnThread); queueRunner_.RunSteps(cmd, stepsOnThread); stepsOnThread.clear(); @@ -677,6 +683,8 @@ void VulkanRenderManager::FlushSync() { Submit(frame, false); + // This is brutal! Should probably wait for a fence instead, not that it'll matter much since we'll + // still stall everything. vkDeviceWaitIdle(vulkan_->GetDevice()); // At this point we can resume filling the command buffers for the current frame since From 6a8f72a327439b488395e2aba6473606ebb3bf60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 31 Oct 2017 23:49:47 +0100 Subject: [PATCH 099/111] Use the global curFrame counter. No need for a vector for pushing cmdbufs. --- GPU/Vulkan/DrawEngineVulkan.cpp | 13 +++++-------- GPU/Vulkan/DrawEngineVulkan.h | 3 +-- GPU/Vulkan/GPU_Vulkan.cpp | 18 +++++++----------- GPU/Vulkan/GPU_Vulkan.h | 1 - GPU/Vulkan/TextureCacheVulkan.cpp | 9 ++++++++- GPU/Vulkan/VulkanUtil.cpp | 11 +++++------ GPU/Vulkan/VulkanUtil.h | 1 - ext/native/thin3d/VulkanQueueRunner.cpp | 1 - ext/native/thin3d/VulkanRenderManager.cpp | 13 ++++++------- ext/native/thin3d/thin3d_vulkan.cpp | 10 +++------- 10 files changed, 35 insertions(+), 45 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index a71ddf4da5..3fc9bd9026 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -79,7 +79,6 @@ enum { DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *draw) : vulkan_(vulkan), draw_(draw), - curFrame_(0), stats_{}, vai_(1024) { decOptions_.expandAllWeightsToFloat = false; @@ -279,7 +278,8 @@ void DrawEngineVulkan::DeviceRestore(VulkanContext *vulkan) { void DrawEngineVulkan::BeginFrame() { lastPipeline_ = nullptr; - FrameData *frame = &frame_[curFrame_]; + int curFrame = vulkan_->GetCurFrame(); + FrameData *frame = &frame_[curFrame]; // First reset all buffers, then begin. This is so that Reset can free memory and Begin can allocate it, // if growing the buffer is needed. Doing it this way will reduce fragmentation if more than one buffer @@ -358,16 +358,13 @@ void DrawEngineVulkan::BeginFrame() { } void DrawEngineVulkan::EndFrame() { - FrameData *frame = &frame_[curFrame_]; + FrameData *frame = &frame_[vulkan_->GetCurFrame()]; stats_.pushUBOSpaceUsed = (int)frame->pushUBO->GetOffset(); stats_.pushVertexSpaceUsed = (int)frame->pushVertex->GetOffset(); stats_.pushIndexSpaceUsed = (int)frame->pushIndex->GetOffset(); frame->pushUBO->End(); frame->pushVertex->End(); frame->pushIndex->End(); - curFrame_++; - if (curFrame_ >= vulkan_->GetInflightFrames()) - curFrame_ = 0; vertexCache_->End(); } @@ -520,7 +517,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView assert(light != VK_NULL_HANDLE); assert(bone != VK_NULL_HANDLE); - FrameData *frame = &frame_[curFrame_]; + FrameData *frame = &frame_[vulkan_->GetCurFrame()]; if (!gstate_c.bezier && !gstate_c.spline) { // Has no cache when HW tessellation. VkDescriptorSet d = frame->descSets.Get(key); if (d != VK_NULL_HANDLE) @@ -668,7 +665,7 @@ void DrawEngineVulkan::DoFlush() { // Since we have a new cmdbuf, dirty our dynamic state so it gets re-set. // gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE|DIRTY_DEPTHSTENCIL_STATE|DIRTY_BLEND_STATE); - FrameData *frame = &frame_[curFrame_]; + FrameData *frame = &frame_[vulkan_->GetCurFrame()]; bool textureNeedsApply = false; if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index e1ebba790f..2322dfdc45 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -168,7 +168,7 @@ public: } VulkanPushBuffer *GetPushBufferForTextureData() { - return frame_[curFrame_].pushUBO; + return frame_[vulkan_->GetCurFrame()].pushUBO; } const DrawEngineVulkanStats &GetStats() const { @@ -233,7 +233,6 @@ private: }; GEPrimitiveType lastPrim_ = GE_PRIM_INVALID; - int curFrame_; FrameData frame_[VulkanContext::MAX_INFLIGHT_FRAMES]; // Other diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 3cb01856d1..7b122bfce0 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -218,16 +218,16 @@ void GPU_Vulkan::BeginHostFrame() { textureCacheVulkan_->StartFrame(); - - FrameData &frame = frameData_[curFrame_]; + int curFrame = vulkan_->GetCurFrame(); + FrameData &frame = frameData_[curFrame]; frame.push_->Reset(); frame.push_->Begin(vulkan_); framebufferManagerVulkan_->BeginFrameVulkan(); - framebufferManagerVulkan_->SetPushBuffer(frameData_[curFrame_].push_); - depalShaderCache_.SetPushBuffer(frameData_[curFrame_].push_); - textureCacheVulkan_->SetPushBuffer(frameData_[curFrame_].push_); + framebufferManagerVulkan_->SetPushBuffer(frameData_[curFrame].push_); + depalShaderCache_.SetPushBuffer(frameData_[curFrame].push_); + textureCacheVulkan_->SetPushBuffer(frameData_[curFrame].push_); vulkan2D_.BeginFrame(); @@ -244,16 +244,12 @@ void GPU_Vulkan::BeginHostFrame() { } void GPU_Vulkan::EndHostFrame() { - FrameData &frame = frameData_[curFrame_]; + int curFrame = vulkan_->GetCurFrame(); + FrameData &frame = frameData_[curFrame]; frame.push_->End(); vulkan2D_.EndFrame(); - curFrame_++; - if (curFrame_ >= vulkan_->GetInflightFrames()) { - curFrame_ = 0; - } - drawEngine_.EndFrame(); framebufferManagerVulkan_->EndFrame(); textureCacheVulkan_->EndFrame(); diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index ccdf36b3a4..4c1d49a470 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -134,5 +134,4 @@ private: }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]{}; - int curFrame_ = 0; }; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 5fc95714ce..6e6feeb05f 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -550,6 +550,11 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm int h = gstate.getTextureHeight(0); ReplacedTexture &replaced = replacer_.FindReplacement(cachekey, entry->fullhash, w, h); if (replaced.GetSize(0, w, h)) { + if (replaceImages) { + // Since we're replacing the texture, we can't replace the image inside. + ReleaseTexture(entry, true); + replaceImages = false; + } // We're replacing, so we won't scale. scaleFactor = 1; entry->status |= TexCacheEntry::STATUS_IS_SCALED; @@ -586,7 +591,9 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm if (replaced.Valid()) { actualFmt = ToVulkanFormat(replaced.Format(0)); } - if (!entry->vkTex) { + + if (!entry->vkTex) { // Change to "if (true) {" to always recreate. + delete entry->vkTex; entry->vkTex = new CachedTextureVulkan(); entry->vkTex->texture_ = new VulkanTexture(vulkan_, allocator_); VulkanTexture *image = entry->vkTex->texture_; diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index af0240f736..f07f6b68c7 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -20,7 +20,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "GPU/Vulkan/VulkanUtil.h" -Vulkan2D::Vulkan2D(VulkanContext *vulkan) : vulkan_(vulkan), curFrame_(0) { +Vulkan2D::Vulkan2D(VulkanContext *vulkan) : vulkan_(vulkan) { InitDeviceObjects(); } @@ -121,15 +121,13 @@ void Vulkan2D::DeviceRestore(VulkanContext *vulkan) { } void Vulkan2D::BeginFrame() { - FrameData &frame = frameData_[curFrame_]; + int curFrame = vulkan_->GetCurFrame(); + FrameData &frame = frameData_[curFrame]; frame.descSets.clear(); vkResetDescriptorPool(vulkan_->GetDevice(), frame.descPool, 0); } void Vulkan2D::EndFrame() { - curFrame_++; - if (curFrame_ >= vulkan_->GetInflightFrames()) - curFrame_ = 0; } VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2) { @@ -139,7 +137,8 @@ VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, key.sampler[0] = sampler1; key.sampler[1] = sampler2; - FrameData *frame = &frameData_[curFrame_]; + int curFrame = vulkan_->GetCurFrame(); + FrameData *frame = &frameData_[curFrame]; auto iter = frame->descSets.find(key); if (iter != frame->descSets.end()) { return iter->second; diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index e9696a7a9c..28bc023109 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -104,7 +104,6 @@ private: }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; - int curFrame_ = 0; std::map pipelines_; }; diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 8a7b4ccafd..1223fd8e33 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -536,7 +536,6 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step } renderPass = renderPasses_[RPIndex(step.render.color, step.render.depthStencil)]; - // VLOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass); if (step.render.color == VKRRenderPassAction::CLEAR) { Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor); numClearVals = 1; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 58ed64bbc1..e3b0fa7745 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -587,15 +587,13 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { VkResult res = vkEndCommandBuffer(frameData.mainCmd); assert(res == VK_SUCCESS); + VkCommandBuffer cmdBufs[2]; int numCmdBufs = 0; - std::vector cmdBufs; - cmdBufs.reserve(2); if (frameData.hasInitCommands) { - cmdBufs.push_back(frameData.initCmd); + cmdBufs[numCmdBufs++] = frameData.initCmd; frameData.hasInitCommands = false; } - - cmdBufs.push_back(frameData.mainCmd); + cmdBufs[numCmdBufs++] = frameData.mainCmd; VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; if (triggerFence) { @@ -605,13 +603,14 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; submit_info.pWaitDstStageMask = waitStage; - submit_info.commandBufferCount = (uint32_t)cmdBufs.size(); - submit_info.pCommandBuffers = cmdBufs.data(); + submit_info.commandBufferCount = (uint32_t)numCmdBufs; + submit_info.pCommandBuffers = cmdBufs; if (triggerFence) { submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; } res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, triggerFence ? frameData.fence : VK_NULL_HANDLE); + if (res == VK_ERROR_DEVICE_LOST) { // _assert_msg_(G3D, false, "Lost the Vulkan device! What did we do wrong?"); ELOG("DEVICE LOST"); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 06d87cfb1e..acd271deec 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -509,7 +509,6 @@ private: FrameData frame_[VulkanContext::MAX_INFLIGHT_FRAMES]{}; - int frameNum_ = 0; VulkanPushBuffer *push_ = nullptr; DeviceCaps caps_{}; @@ -655,7 +654,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc) { } VKContext::VKContext(VulkanContext *vulkan) - : vulkan_(vulkan), frameNum_(0), caps_{}, renderManager_(vulkan) { + : vulkan_(vulkan), caps_{}, renderManager_(vulkan) { caps_.anisoSupported = vulkan->GetFeaturesAvailable().samplerAnisotropy != 0; caps_.geometryShaderSupported = vulkan->GetFeaturesAvailable().geometryShader != 0; caps_.tesselationShaderSupported = vulkan->GetFeaturesAvailable().tessellationShader != 0; @@ -742,7 +741,7 @@ VKContext::~VKContext() { void VKContext::BeginFrame() { renderManager_.BeginFrame(); - FrameData &frame = frame_[frameNum_]; + FrameData &frame = frame_[vulkan_->GetCurFrame()]; push_ = frame.pushBuffer; // OK, we now know that nothing is reading from this frame's data pushbuffer, @@ -764,16 +763,13 @@ void VKContext::EndFrame() { renderManager_.Finish(); - frameNum_++; - if (frameNum_ >= vulkan_->GetInflightFrames()) - frameNum_ = 0; push_ = nullptr; } VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { DescriptorSetKey key; - FrameData *frame = &frame_[frameNum_]; + FrameData *frame = &frame_[vulkan_->GetCurFrame()]; key.texture_ = boundTextures_[0]; key.sampler_ = boundSamplers_[0]; From 700d6d10f4d4cbc63f1df9ad2c117f44e479c192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 00:09:04 +0100 Subject: [PATCH 100/111] Always recreate DrawPixelsTex to avoid inter-frame races. --- GPU/Vulkan/FramebufferVulkan.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index d1bf945bd0..83bda7596d 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -206,22 +206,17 @@ void FramebufferManagerVulkan::Init() { } void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - if (drawPixelsTex_ && (drawPixelsTexFormat_ != srcPixelFormat || drawPixelsTex_->GetWidth() != width || drawPixelsTex_->GetHeight() != height)) { + if (drawPixelsTex_) { delete drawPixelsTex_; drawPixelsTex_ = nullptr; } VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); - if (!drawPixelsTex_) { - drawPixelsTex_ = new VulkanTexture(vulkan_); - drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); - // Initialize backbuffer texture for DrawPixels - drawPixelsTexFormat_ = srcPixelFormat; - } else { - // TODO: We may want to double-buffer these, when we have more frames hanging about. - drawPixelsTex_->TransitionForUpload(initCmd); - } + drawPixelsTex_ = new VulkanTexture(vulkan_); + drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + // Initialize backbuffer texture for DrawPixels + drawPixelsTexFormat_ = srcPixelFormat; // TODO: We can just change the texture format and flip some bits around instead of this. // Could share code with the texture cache perhaps. From d0f328213a813b0b4791e83328568ccdb2705acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 00:37:07 +0100 Subject: [PATCH 101/111] Add some disabled code to submit "init" command buffers separately to the queue for debugging. --- ext/native/thin3d/VulkanRenderManager.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index e3b0fa7745..1f177f0fe7 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -593,16 +593,28 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { cmdBufs[numCmdBufs++] = frameData.initCmd; frameData.hasInitCommands = false; } + if (false) { + // Send the init commands off separately. Used this once to confirm that the cause of a device loss was in the init cmdbuf. + VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.commandBufferCount = (uint32_t)numCmdBufs; + submit_info.pCommandBuffers = cmdBufs; + res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, VK_NULL_HANDLE); + if (res == VK_ERROR_DEVICE_LOST) { + _assert_msg_(G3D, false, "Lost the Vulkan device!"); + } else { + assert(res == VK_SUCCESS); + } + numCmdBufs = 0; + } cmdBufs[numCmdBufs++] = frameData.mainCmd; VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; if (triggerFence) { submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &acquireSemaphore_; + VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submit_info.pWaitDstStageMask = waitStage; } - - VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }; - submit_info.pWaitDstStageMask = waitStage; submit_info.commandBufferCount = (uint32_t)numCmdBufs; submit_info.pCommandBuffers = cmdBufs; if (triggerFence) { @@ -610,10 +622,8 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { submit_info.pSignalSemaphores = &renderingCompleteSemaphore_; } res = vkQueueSubmit(vulkan_->GetGraphicsQueue(), 1, &submit_info, triggerFence ? frameData.fence : VK_NULL_HANDLE); - if (res == VK_ERROR_DEVICE_LOST) { - // _assert_msg_(G3D, false, "Lost the Vulkan device! What did we do wrong?"); - ELOG("DEVICE LOST"); + _assert_msg_(G3D, false, "Lost the Vulkan device!"); } else { assert(res == VK_SUCCESS); } From 96cd36887825ee225f51c4c2c5edb598c3828e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 01:17:01 +0100 Subject: [PATCH 102/111] Make VulkanTexture "immutable". --- Common/Vulkan/VulkanImage.cpp | 8 -------- Common/Vulkan/VulkanImage.h | 3 --- GPU/Vulkan/FramebufferVulkan.cpp | 8 +++++++- GPU/Vulkan/TextureCacheVulkan.cpp | 4 +--- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp index 0c0a5ff061..75f78f95b4 100644 --- a/Common/Vulkan/VulkanImage.cpp +++ b/Common/Vulkan/VulkanImage.cpp @@ -358,14 +358,6 @@ void VulkanTexture::EndCreate(VkCommandBuffer cmd) { VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); } -void VulkanTexture::TransitionForUpload(VkCommandBuffer cmd) { - TransitionImageLayout2(cmd, image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); -} - void VulkanTexture::Destroy() { if (view != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeleteImageView(view); diff --git a/Common/Vulkan/VulkanImage.h b/Common/Vulkan/VulkanImage.h index 7a6bff62f6..cc303a7460 100644 --- a/Common/Vulkan/VulkanImage.h +++ b/Common/Vulkan/VulkanImage.h @@ -35,9 +35,6 @@ public: void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels void EndCreate(VkCommandBuffer cmd); - // Dangerous. - void TransitionForUpload(VkCommandBuffer cmd); - int GetNumMips() const { return numMips_; } void Destroy(); diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 83bda7596d..e8d5c2e3ef 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -214,7 +214,13 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); drawPixelsTex_ = new VulkanTexture(vulkan_); - drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + if (!drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)) { + // out of memory? + delete drawPixelsTex_; + drawPixelsTex_ = nullptr; + overrideImageView_ = VK_NULL_HANDLE; + return; + } // Initialize backbuffer texture for DrawPixels drawPixelsTexFormat_ = srcPixelFormat; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 6e6feeb05f..9869c40a9e 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -592,7 +592,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm actualFmt = ToVulkanFormat(replaced.Format(0)); } - if (!entry->vkTex) { // Change to "if (true) {" to always recreate. + { delete entry->vkTex; entry->vkTex = new CachedTextureVulkan(); entry->vkTex->texture_ = new VulkanTexture(vulkan_, allocator_); @@ -643,8 +643,6 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm delete entry->vkTex; entry->vkTex = nullptr; } - } else { - entry->vkTex->texture_->TransitionForUpload(cmdInit); } lastBoundTexture = entry->vkTex; From d92d78f54e62ccc52dff4de2614504fbaa384e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 01:40:26 +0100 Subject: [PATCH 103/111] Vulkan: Fix colors for low-bpp depal (actually fixing the shadows of ff-type0) --- GPU/Vulkan/DepalettizeShaderVulkan.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index 322e0e292c..406afe614c 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -35,15 +35,24 @@ void main() { } )"; -static VkFormat GetClutDestFormat(GEPaletteFormat format) { +static const VkComponentMapping VULKAN_4444_SWIZZLE = { VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B }; +static const VkComponentMapping VULKAN_1555_SWIZZLE = { VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A }; +static const VkComponentMapping VULKAN_565_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; +static const VkComponentMapping VULKAN_8888_SWIZZLE = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + +static VkFormat GetClutDestFormat(GEPaletteFormat format, VkComponentMapping *componentMapping) { switch (format) { case GE_CMODE_16BIT_ABGR4444: - return VK_FORMAT_R4G4B4A4_UNORM_PACK16; + *componentMapping = VULKAN_4444_SWIZZLE; + return VK_FORMAT_B4G4R4A4_UNORM_PACK16; case GE_CMODE_16BIT_ABGR5551: + *componentMapping = VULKAN_1555_SWIZZLE; return VK_FORMAT_A1R5G5B5_UNORM_PACK16; case GE_CMODE_16BIT_BGR5650: - return VK_FORMAT_R5G6B5_UNORM_PACK16; + *componentMapping = VULKAN_565_SWIZZLE; + return VK_FORMAT_B5G6R5_UNORM_PACK16; case GE_CMODE_32BIT_ABGR8888: + *componentMapping = VULKAN_8888_SWIZZLE; return VK_FORMAT_R8G8B8A8_UNORM; } return VK_FORMAT_UNDEFINED; @@ -104,7 +113,8 @@ VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat return oldtex->second->texture; } - VkFormat destFormat = GetClutDestFormat(clutFormat); + VkComponentMapping componentMapping; + VkFormat destFormat = GetClutDestFormat(clutFormat, &componentMapping); int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512; VkBuffer pushBuffer; @@ -112,7 +122,7 @@ VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat VulkanTexture *vktex = new VulkanTexture(vulkan_, alloc_); if (!vktex->CreateDirect(cmd, texturePixels, 1, 1, destFormat, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, nullptr)) { + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, &componentMapping)) { Crash(); } vktex->UploadMip(cmd, 0, texturePixels, 1, pushBuffer, pushOffset, texturePixels); From 6b904547b2e179f0c8a99ce5f7a8d51dad534304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 08:57:31 +0100 Subject: [PATCH 104/111] Complete a comment. --- ext/native/thin3d/VulkanRenderManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 1f177f0fe7..9b05e383cc 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -555,6 +555,8 @@ void VulkanRenderManager::Finish() { } // Can be called multiple times with no bad side effects. This is so that we can either begin a frame the normal way, +// or stop it in the middle for a synchronous readback, then start over again mostly normally but without repeating +// the backbuffer image acquisition. void VulkanRenderManager::BeginSubmitFrame(int frame) { FrameData &frameData = frameData_[frame]; if (!frameData.hasBegun) { From 94b16bf097217fa53e49d6ac91f6b8fe29583294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 10:09:16 +0100 Subject: [PATCH 105/111] DenseHashMap: Speed up clearing. Became more important since I had to disable keeping descriptor sets around between frames. --- Common/Hashmaps.h | 84 +++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/Common/Hashmaps.h b/Common/Hashmaps.h index c8ec27bb09..687b5c6994 100644 --- a/Common/Hashmaps.h +++ b/Common/Hashmaps.h @@ -20,7 +20,7 @@ inline bool KeyEquals(const K &a, const K &b) { return !memcmp(&a, &b, sizeof(K)); } -enum class BucketState { +enum class BucketState : uint8_t { FREE, TAKEN, REMOVED, // for linear probing to work (and removal during deletion) we need tombstones @@ -35,6 +35,7 @@ class DenseHashMap { public: DenseHashMap(int initialCapacity) : capacity_(initialCapacity) { map.resize(initialCapacity); + state.resize(initialCapacity); } // Returns nullptr if no entry was found. @@ -44,9 +45,9 @@ public: // No? Let's go into search mode. Linear probing. uint32_t p = pos; while (true) { - if (map[p].state == BucketState::TAKEN && KeyEquals(key, map[p].key)) + if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) return map[p].value; - else if (map[p].state == BucketState::FREE) + else if (state[p] == BucketState::FREE) return NullValue; p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. if (p == pos) @@ -65,7 +66,7 @@ public: uint32_t pos = HashKey(key) & mask; uint32_t p = pos; while (true) { - if (map[p].state == BucketState::TAKEN) { + if (state[p] == BucketState::TAKEN) { if (KeyEquals(key, map[p].key)) { Crash(); // Bad! We already got this one. Let's avoid this case. return false; @@ -81,10 +82,10 @@ public: Crash(); } } - if (map[p].state == BucketState::REMOVED) { + if (state[p] == BucketState::REMOVED) { removedCount_--; } - map[p].state = BucketState::TAKEN; + state[p] = BucketState::TAKEN; map[p].key = key; map[p].value = value; count_++; @@ -95,10 +96,10 @@ public: uint32_t mask = capacity_ - 1; uint32_t pos = HashKey(key) & mask; uint32_t p = pos; - while (map[p].state != BucketState::FREE) { - if (map[p].state == BucketState::TAKEN && KeyEquals(key, map[p].key)) { + while (state[p] != BucketState::FREE) { + if (state[p] == BucketState::TAKEN && KeyEquals(key, map[p].key)) { // Got it! Mark it as removed. - map[p].state = BucketState::REMOVED; + state[p] = BucketState::REMOVED; removedCount_++; count_--; return true; @@ -118,17 +119,17 @@ public: template inline void Iterate(T func) { - for (auto &iter : map) { - if (iter.state == BucketState::TAKEN) { - func(iter.key, iter.value); + for (size_t i = 0; i < map.size(); i++) { + if (state[i] == BucketState::TAKEN) { + func(map[i].key, map[i].value); } } } void Clear() { - // TODO: Speedup? - map.clear(); - map.resize(capacity_); + for (int i = 0; i < state.size(); i++) { + state[i] = BucketState::FREE; + } } void Rebuild() { @@ -147,23 +148,24 @@ private: // We simply move out the existing data, then we re-insert the old. // This is extremely non-atomic and will need synchronization. std::vector old = std::move(map); + std::vector oldState = std::move(state); capacity_ *= factor; - map.clear(); map.resize(capacity_); + state.resize(capacity_); count_ = 0; // Insert will update it. removedCount_ = 0; - for (auto &iter : old) { - if (iter.state == BucketState::TAKEN) { - Insert(iter.key, iter.value); + for (size_t i = 0; i < old.size(); i++) { + if (oldState[i] == BucketState::TAKEN) { + Insert(old[i].key, old[i].value); } } } struct Pair { - BucketState state; Key key; Value value; }; std::vector map; + std::vector state; int capacity_; int count_ = 0; int removedCount_ = 0; @@ -176,6 +178,7 @@ class PrehashMap { public: PrehashMap(int initialCapacity) : capacity_(initialCapacity) { map.resize(initialCapacity); + state.resize(initialCapacity); } // Returns nullptr if no entry was found. @@ -185,9 +188,9 @@ public: // No? Let's go into search mode. Linear probing. uint32_t p = pos; while (true) { - if (map[p].state == BucketState::TAKEN && hash == map[p].hash) + if (state[p] == BucketState::TAKEN && hash == map[p].hash) return map[p].value; - else if (map[p].state == BucketState::FREE) + else if (state[p] == BucketState::FREE) return NullValue; p = (p + 1) & mask; // If the state is REMOVED, we just keep on walking. if (p == pos) @@ -205,8 +208,8 @@ public: uint32_t mask = capacity_ - 1; uint32_t pos = hash & mask; uint32_t p = pos; - while (map[p].state != BucketState::FREE) { - if (map[p].state == BucketState::TAKEN) { + while (state[p] != BucketState::FREE) { + if (state[p] == BucketState::TAKEN) { if (hash == map[p].hash) return false; // Bad! } else { @@ -219,10 +222,10 @@ public: Crash(); } } - if (map[p].state == BucketState::REMOVED) { + if (state[p] == BucketState::REMOVED) { removedCount_--; } - map[p].state = BucketState::TAKEN; + state[p] = BucketState::TAKEN; map[p].hash = hash; map[p].value = value; count_++; @@ -233,10 +236,10 @@ public: uint32_t mask = capacity_ - 1; uint32_t pos = hash & mask; uint32_t p = pos; - while (map[p].state != BucketState::FREE) { - if (map[p].state == BucketState::TAKEN && hash == map[p].hash) { + while (state[p] != BucketState::FREE) { + if (state[p] == BucketState::TAKEN && hash == map[p].hash) { // Got it! - map[p].state = BucketState::REMOVED; + state[p] = BucketState::REMOVED; removedCount_++; count_--; return true; @@ -255,17 +258,17 @@ public: template void Iterate(T func) { - for (auto &iter : map) { - if (iter.state == BucketState::TAKEN) { - func(iter.hash, iter.value); + for (size_t i = 0; i < map.size(); i++) { + if (state[i] == BucketState::TAKEN) { + func(map[i].hash, map[i].value); } } } void Clear() { - // TODO: Speedup? - map.clear(); - map.resize(capacity_); + for (int i = 0; i < state.size(); i++) { + state[i] = BucketState::FREE; + } } // Gets rid of REMOVED tombstones, making lookups somewhat more efficient. @@ -285,23 +288,24 @@ private: // We simply move out the existing data, then we re-insert the old. // This is extremely non-atomic and will need synchronization. std::vector old = std::move(map); + std::vector oldState = std::move(state); capacity_ *= factor; - map.clear(); map.resize(capacity_); + state.resize(capacity_); count_ = 0; // Insert will update it. removedCount_ = 0; - for (auto &iter : old) { - if (iter.state == BucketState::TAKEN) { - Insert(iter.hash, iter.value); + for (size_t i = 0; i < old.size(); i++) { + if (oldState[i] == BucketState::TAKEN) { + Insert(old[i].hash, old[i].value); } } } struct Pair { - BucketState state; uint32_t hash; Value value; }; std::vector map; + std::vector state; int capacity_; int count_ = 0; int removedCount_ = 0; From 533f80a056e65dd1a75bad6409fbd0ca56d3305c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 11:47:05 +0100 Subject: [PATCH 106/111] Fix bug in Vulkan's vertex shader generator, causing stretched characters in Jeanne D'arc. --- GPU/Directx9/VertexShaderGeneratorDX9.cpp | 5 +++-- GPU/Vulkan/GPU_Vulkan.cpp | 2 +- GPU/Vulkan/VertexShaderGeneratorVulkan.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 01fd322a5e..2a32a3ca4f 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -90,8 +90,6 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l bool hasTexcoordTess = id.Bit(VS_BIT_HAS_TEXCOORD_TESS); bool flipNormalTess = id.Bit(VS_BIT_NORM_REVERSE_TESS); - bool scaleUV = !throughmode && (uvGenMode == GE_TEXMAP_TEXTURE_COORDS || uvGenMode == GE_TEXMAP_UNKNOWN); - DoLightComputation doLight[4] = { LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF }; if (useHWTransform) { int shadeLight0 = doShadeMapping ? ls0 : -1; @@ -723,6 +721,9 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l WRITE(p, " Out.v_color1 = float3(0, 0, 0);\n"); } + bool scaleUV = !throughmode && (uvGenMode == GE_TEXMAP_TEXTURE_COORDS || uvGenMode == GE_TEXMAP_UNKNOWN); + + // Step 3: UV generation if (doTexture) { switch (uvGenMode) { diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 7b122bfce0..5fdf6d7add 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -624,7 +624,6 @@ void GPU_Vulkan::Execute_Bezier(u32 op, u32 diff) { int bz_vcount = (op >> 8) & 0xFF; bool computeNormals = gstate.isLightingEnabled(); bool patchFacing = gstate.patchfacing & 1; - int bytesRead = 0; if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); @@ -635,6 +634,7 @@ void GPU_Vulkan::Execute_Bezier(u32 op, u32 diff) { } } + int bytesRead = 0; UpdateUVScaleOffset(); drawEngine_.SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index b3e9ae7e13..e745e42522 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -626,7 +626,7 @@ bool GenerateVulkanGLSLVertexShader(const ShaderID &id, char *buffer, bool *uses if (doBezier || doSpline) WRITE(p, " v_texcoord = vec3(tex.xy * base.uvscaleoffset.xy + base.uvscaleoffset.zw, 0.0);\n"); else - WRITE(p, " v_texcoord = vec3(texcoord.xy, 0.0);\n"); + WRITE(p, " v_texcoord = vec3(texcoord.xy * base.uvscaleoffset.xy, 0.0);\n"); } else { WRITE(p, " v_texcoord = vec3(0.0);\n"); } From ca7a2d06cadeb716c20a035f4bc46aaa667ebb3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 14:18:39 +0100 Subject: [PATCH 107/111] Vulkan: Implement stencil upload (for Star Ocean). --- CMakeLists.txt | 1 + GPU/GPU.vcxproj | 1 + GPU/GPU.vcxproj.filters | 3 + GPU/Vulkan/FramebufferVulkan.cpp | 10 +- GPU/Vulkan/FramebufferVulkan.h | 3 + GPU/Vulkan/StencilBufferVulkan.cpp | 219 ++++++++++++++++++++++++ GPU/Vulkan/VulkanUtil.cpp | 44 +++-- GPU/Vulkan/VulkanUtil.h | 14 +- android/jni/Android.mk | 1 + ext/native/thin3d/VulkanQueueRunner.cpp | 12 +- ext/native/thin3d/VulkanQueueRunner.h | 12 +- ext/native/thin3d/VulkanRenderManager.h | 12 ++ ext/native/thin3d/thin3d_vulkan.cpp | 4 +- 13 files changed, 308 insertions(+), 28 deletions(-) create mode 100644 GPU/Vulkan/StencilBufferVulkan.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2135eb8ba2..a1fe81d564 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1174,6 +1174,7 @@ set(GPU_VULKAN GPU/Vulkan/ShaderManagerVulkan.h GPU/Vulkan/StateMappingVulkan.cpp GPU/Vulkan/StateMappingVulkan.h + GPU/Vulkan/StencilBufferVulkan.cpp GPU/Vulkan/TextureCacheVulkan.cpp GPU/Vulkan/TextureCacheVulkan.h GPU/Vulkan/TextureScalerVulkan.cpp diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index b53ade9739..30c0bd2152 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -366,6 +366,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index cbbd189fe9..ca0ce92bff 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -525,5 +525,8 @@ Debugger + + Vulkan + \ No newline at end of file diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index e8d5c2e3ef..6abc89637b 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -147,6 +147,10 @@ void FramebufferManagerVulkan::DestroyDeviceObjects() { vulkan_->Delete().QueueDeleteShaderModule(fsBasicTex_); if (vsBasicTex_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteShaderModule(vsBasicTex_); + if (stencilFs_ != VK_NULL_HANDLE) + vulkan_->Delete().QueueDeleteShaderModule(stencilFs_); + if (stencilVs_ != VK_NULL_HANDLE) + vulkan_->Delete().QueueDeleteShaderModule(stencilVs_); if (linearSampler_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteSampler(linearSampler_); @@ -369,12 +373,6 @@ void FramebufferManagerVulkan::RebindFramebuffer() { gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); } -bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skipZero) { - // In Vulkan we should be able to simply copy the stencil data directly to a stencil buffer without - // messing about with bitplane textures and the like. Or actually, maybe not... - return false; -} - int FramebufferManagerVulkan::GetLineWidth() { if (g_Config.iInternalResolution == 0) { return std::max(1, (int)(renderWidth_ / 480)); diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 2ea5037c6b..9c37034842 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -134,6 +134,9 @@ private: VkShaderModule fsBasicTex_; VkShaderModule vsBasicTex_; + VkShaderModule stencilVs_ = VK_NULL_HANDLE; + VkShaderModule stencilFs_ = VK_NULL_HANDLE; + VkPipeline cur2DPipeline_ = VK_NULL_HANDLE; // Postprocessing diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp new file mode 100644 index 0000000000..313c8f309e --- /dev/null +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -0,0 +1,219 @@ +// Copyright (c) 2014- PPSSPP Project. + +// This program 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, version 2.0 or later versions. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "base/logging.h" + +#include "ext/native/thin3d/thin3d.h" +#include "ext/native/thin3d/VulkanRenderManager.h" +#include "Core/Reporting.h" +#include "GPU/Vulkan/FramebufferVulkan.h" +#include "GPU/Vulkan/FragmentShaderGeneratorVulkan.h" +#include "GPU/Vulkan/ShaderManagerVulkan.h" +#include "GPU/Vulkan/TextureCacheVulkan.h" +#include "GPU/Vulkan/VulkanUtil.h" + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +struct StencilValueUB { + uint32_t u_stencilValue[4]; +}; + +static const char *stencil_fs = R"(#version 400 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +layout (binding = 0) uniform sampler2D tex; +layout(push_constant) uniform params { + int u_stencilValue; +}; +layout (location = 0) in vec2 v_texcoord0; +layout (location = 0) out vec4 fragColor0; + +void main() { + vec4 index = texture(tex, v_texcoord0); + int indexBits = int(floor(index.a * 255.99)) & 0xFF; + if ((indexBits & u_stencilValue) == 0) + discard; + fragColor0 = index.aaaa; +} +)"; + +static const char stencil_vs[] = R"(#version 400 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +layout (location = 0) out vec2 v_texcoord0; +out gl_PerVertex { vec4 gl_Position; }; +void main() { + int id = gl_VertexIndex; + v_texcoord0.x = (id == 2) ? 2.0 : 0.0; + v_texcoord0.y = (id == 1) ? 2.0 : 0.0; + gl_Position = vec4(v_texcoord0 * vec2(2.0, 2.0) + vec2(-1.0, -1.0), 0.0, 1.0); +} +)"; + +static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + + for (u32 i = 0; i < numPixels / 2; ++i) { + if (ptr[i] & 0x80008000) { + return 1; + } + } + return 0; +} + +static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + u32 bits = 0; + + for (u32 i = 0; i < numPixels / 2; ++i) { + bits |= ptr[i]; + } + + return ((bits >> 12) & 0xF) | (bits >> 28); +} + +static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + u32 bits = 0; + + for (u32 i = 0; i < numPixels; ++i) { + bits |= ptr[i]; + } + + return bits >> 24; +} + +// In Vulkan we should be able to simply copy the stencil data directly to a stencil buffer without +// messing about with bitplane textures and the like. Or actually, maybe not... Let's start with +// the traditional approach. +bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skipZero) { + if (!MayIntersectFramebuffer(addr)) { + return false; + } + + VirtualFramebuffer *dstBuffer = 0; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + if (MaskedEqual(vfb->fb_address, addr)) { + dstBuffer = vfb; + } + } + if (!dstBuffer) { + return false; + } + + int values = 0; + u8 usedBits = 0; + + const u8 *src = Memory::GetPointer(addr); + if (!src) { + return false; + } + + switch (dstBuffer->format) { + case GE_FORMAT_565: + // Well, this doesn't make much sense. + return false; + case GE_FORMAT_5551: + usedBits = StencilBits5551(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 2; + break; + case GE_FORMAT_4444: + usedBits = StencilBits4444(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 16; + break; + case GE_FORMAT_8888: + usedBits = StencilBits8888(src, dstBuffer->fb_stride * dstBuffer->bufferHeight); + values = 256; + break; + case GE_FORMAT_INVALID: + // Impossible. + break; + } + + std::string error; + if (!stencilVs_) { + stencilVs_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, stencil_vs, &error); + stencilFs_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, stencil_fs, &error); + } + VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS); + + VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + + if (usedBits == 0) { + if (skipZero) { + // Common when creating buffers, it's already 0. We're done. + return false; + } + + // TODO: Find a nice way to clear alpha here too. + draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); + return true; + } + + shaderManagerVulkan_->DirtyLastShader(); + textureCacheVulkan_->ForgetLastTexture(); + + u16 w = dstBuffer->renderWidth; + u16 h = dstBuffer->renderHeight; + float u1 = 1.0f; + float v1 = 1.0f; + MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); + if (dstBuffer->fbo) { + draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); + } else { + // something is wrong... + } + + VkPipeline pipeline = vulkan2D_->GetPipeline(rp, stencilVs_, stencilFs_, false, Vulkan2D::VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS); + renderManager->BindPipeline(pipeline); + renderManager->SetViewport({ 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f }); + renderManager->SetScissor({ { 0, 0, },{ (uint32_t)w, (uint32_t)h } }); + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE); + + VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(overrideImageView_, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); + + for (int i = 1; i < values; i += i) { + if (!(usedBits & i)) { + // It's already zero, let's skip it. + continue; + } + // These feel a little backwards : Mask is the bits that are going to be written, while value + // is the "mask" that will be tested against. + uint8_t mask = 0; + uint32_t value = 0; + if (dstBuffer->format == GE_FORMAT_4444) { + mask = i | (i << 4); + value = i * 16; + } else if (dstBuffer->format == GE_FORMAT_5551) { + mask = 0xFF; + value = i * 128; + } else { + mask = i; + value = i; + } + renderManager->SetStencilParams(mask, 0xFF, 0xFF); + renderManager->PushConstants(vulkan2D_->GetPipelineLayout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, 4, &value); + renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, VK_NULL_HANDLE, 0, 3); // full screen triangle + } + + overrideImageView_ = VK_NULL_HANDLE; + RebindFramebuffer(); + return true; +} diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index f07f6b68c7..14ba80dccb 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -83,12 +83,12 @@ void Vulkan2D::InitDeviceObjects() { assert(VK_SUCCESS == res); VkDescriptorPoolSize dpTypes[1]; - dpTypes[0].descriptorCount = 1500; + dpTypes[0].descriptorCount = 3000; dpTypes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; dp.flags = 0; // Don't want to mess around with individually freeing these, let's go fixed each frame and zap the whole array. Might try the dynamic approach later. - dp.maxSets = 1500; + dp.maxSets = 3000; dp.pPoolSizes = dpTypes; dp.poolSizeCount = ARRAY_SIZE(dpTypes); for (int i = 0; i < ARRAY_SIZE(frameData_); i++) { @@ -98,7 +98,7 @@ void Vulkan2D::InitDeviceObjects() { VkPushConstantRange push = {}; push.offset = 0; - push.size = 32; + push.size = 16; push.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; @@ -195,11 +195,13 @@ VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, return desc; } -VkPipeline Vulkan2D::GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs) { +VkPipeline Vulkan2D::GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs, bool readVertices, VK2DDepthStencilMode depthStencilMode) { PipelineKey key; key.vs = vs; key.fs = fs; key.rp = rp; + key.depthStencilMode = depthStencilMode; + key.readVertices = readVertices; auto iter = pipelines_.find(key); if (iter != pipelines_.end()) { @@ -208,7 +210,7 @@ VkPipeline Vulkan2D::GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderMod VkPipelineColorBlendAttachmentState blend0 = {}; blend0.blendEnable = false; - blend0.colorWriteMask = 0xF; + blend0.colorWriteMask = depthStencilMode == VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS ? 0 : 0xF; VkPipelineColorBlendStateCreateInfo cbs = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; cbs.pAttachments = &blend0; @@ -217,13 +219,32 @@ VkPipeline Vulkan2D::GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderMod VkPipelineDepthStencilStateCreateInfo dss = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; dss.depthBoundsTestEnable = false; - dss.stencilTestEnable = false; dss.depthTestEnable = false; + dss.stencilTestEnable = false; + switch (depthStencilMode) { + case VK2DDepthStencilMode::NONE: + break; + case VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS: + dss.stencilTestEnable = true; + dss.front.reference = 0xFF; + dss.front.compareMask = 0xFF; + dss.front.compareOp = VK_COMPARE_OP_ALWAYS; + dss.front.depthFailOp = VK_STENCIL_OP_REPLACE; + dss.front.failOp = VK_STENCIL_OP_REPLACE; + dss.front.passOp = VK_STENCIL_OP_REPLACE; + dss.back = dss.front; + break; + } - VkDynamicState dynamicStates[2]; + VkDynamicState dynamicStates[5]; int numDyn = 0; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_SCISSOR; dynamicStates[numDyn++] = VK_DYNAMIC_STATE_VIEWPORT; + if (depthStencilMode == VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS) { + dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK; + dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK; + dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE; + } VkPipelineDynamicStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; ds.pDynamicStates = dynamicStates; @@ -270,10 +291,10 @@ VkPipeline Vulkan2D::GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderMod ibd.stride = vertexStride; VkPipelineVertexInputStateCreateInfo vis = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; - vis.vertexBindingDescriptionCount = 1; - vis.pVertexBindingDescriptions = &ibd; - vis.vertexAttributeDescriptionCount = attributeCount; - vis.pVertexAttributeDescriptions = attrs; + vis.vertexBindingDescriptionCount = readVertices ? 1 : 0; + vis.pVertexBindingDescriptions = readVertices ? &ibd : nullptr; + vis.vertexAttributeDescriptionCount = readVertices ? attributeCount : 0; + vis.pVertexAttributeDescriptions = readVertices ? attrs : nullptr; VkPipelineViewportStateCreateInfo views = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; views.viewportCount = 1; @@ -291,7 +312,6 @@ VkPipeline Vulkan2D::GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderMod pipe.pDepthStencilState = &dss; pipe.pRasterizationState = &rs; - // We will use dynamic viewport state. pipe.pVertexInputState = &vis; pipe.pViewportState = &views; pipe.pTessellationState = nullptr; diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 28bc023109..6a6f526236 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -47,6 +47,8 @@ // No UBO data is used, only PushConstants. // No transform matrices, only post-proj coordinates. // Two textures can be sampled. +// Some simplified depth/stencil modes available. + class Vulkan2D { public: Vulkan2D(VulkanContext *vulkan); @@ -56,8 +58,14 @@ public: void DeviceRestore(VulkanContext *vulkan); void Shutdown(); + enum class VK2DDepthStencilMode { + NONE, + STENCIL_REPLACE_ALWAYS, // Does not draw to color. + }; + // The only supported primitive is the triangle strip, for simplicity. - VkPipeline GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs); + // ReadVertices can be used for vertex-less rendering where you generate verts in the vshader. + VkPipeline GetPipeline(VkRenderPass rp, VkShaderModule vs, VkShaderModule fs, bool readVertices = true, VK2DDepthStencilMode depthStencilMode = VK2DDepthStencilMode::NONE); VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } void BeginFrame(); void EndFrame(); @@ -93,8 +101,10 @@ private: VkShaderModule vs; VkShaderModule fs; VkRenderPass rp; + VK2DDepthStencilMode depthStencilMode; + bool readVertices; bool operator < (const PipelineKey &other) const { - return std::tie(vs, fs, rp) < std::tie(other.vs, other.fs, other.rp); + return std::tie(vs, fs, rp, depthStencilMode, readVertices) < std::tie(other.vs, other.fs, other.rp, depthStencilMode, readVertices); } }; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 8cd5ba7603..d5667771e3 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -136,6 +136,7 @@ VULKAN_FILES := \ $(SRC)/GPU/Vulkan/PipelineManagerVulkan.cpp \ $(SRC)/GPU/Vulkan/ShaderManagerVulkan.cpp \ $(SRC)/GPU/Vulkan/StateMappingVulkan.cpp \ + $(SRC)/GPU/Vulkan/StencilBufferVulkan.cpp \ $(SRC)/GPU/Vulkan/TextureCacheVulkan.cpp \ $(SRC)/GPU/Vulkan/TextureScalerVulkan.cpp \ $(SRC)/GPU/Vulkan/DepalettizeShaderVulkan.cpp \ diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 1223fd8e33..83511ecca9 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -259,6 +259,9 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass) { case VKRRenderCommand::VIEWPORT: ILOG(" Viewport(%f, %f, %f, %f, %f, %f)", cmd.viewport.vp.x, cmd.viewport.vp.y, cmd.viewport.vp.width, cmd.viewport.vp.height, cmd.viewport.vp.minDepth, cmd.viewport.vp.maxDepth); break; + case VKRRenderCommand::PUSH_CONSTANTS: + ILOG(" PushConstants(%d)", cmd.push.size); + break; } } ILOG("RenderPass End(%x)", fb); @@ -334,6 +337,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c VKRFramebuffer *fb = step.render.framebuffer; VkPipeline lastPipeline = VK_NULL_HANDLE; + VkDescriptorSet lastDescSet = VK_NULL_HANDLE; auto &commands = step.commands; @@ -362,6 +366,10 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c vkCmdSetBlendConstants(cmd, c.blendColor.color); break; + case VKRRenderCommand::PUSH_CONSTANTS: + vkCmdPushConstants(cmd, c.push.pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data); + break; + case VKRRenderCommand::STENCIL: vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilWriteMask); vkCmdSetStencilCompareMask(cmd, VK_STENCIL_FRONT_AND_BACK, c.stencil.stencilCompareMask); @@ -377,7 +385,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c case VKRRenderCommand::DRAW: vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets); - vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); + if (c.draw.vbuffer) { + vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset); + } vkCmdDraw(cmd, c.draw.count, 1, 0, 0); break; diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index a35a664f35..06a85a0721 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -18,6 +18,7 @@ enum class VKRRenderCommand : uint8_t { CLEAR, DRAW, DRAW_INDEXED, + PUSH_CONSTANTS, }; struct VkRenderData { @@ -69,11 +70,12 @@ struct VkRenderData { float color[4]; } blendColor; struct { - - } beginRp; - struct { - - } endRp; + VkPipelineLayout pipelineLayout; + VkShaderStageFlags stages; + uint8_t offset; + uint8_t size; + uint8_t data[32]; // Should be enough for now. + } push; }; }; diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index a9d94f05fa..e808416d9c 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -127,6 +127,18 @@ public: curRenderStep_->commands.push_back(data); } + void PushConstants(VkPipelineLayout pipelineLayout, VkShaderStageFlags stages, int offset, int size, void *constants) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); + assert(size + offset < 32); + VkRenderData data{ VKRRenderCommand::PUSH_CONSTANTS }; + data.push.pipelineLayout = pipelineLayout; + data.push.stages = stages; + data.push.offset = offset; + data.push.size = size; + memcpy(data.push.data, constants, size); + curRenderStep_->commands.push_back(data); + } + void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask); void Draw(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count) { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index acd271deec..3ed89d9d78 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -1298,8 +1298,8 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { VKFramebuffer *fb = (VKFramebuffer *)fbo; - VKRRenderPassAction color = (VKRRenderPassAction)rp.color; // same values. - VKRRenderPassAction depth = (VKRRenderPassAction)rp.color; // same values. + VKRRenderPassAction color = (VKRRenderPassAction)rp.color; + VKRRenderPassAction depth = (VKRRenderPassAction)rp.depth; renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); curFramebuffer_ = fb; From 41089ca2841d76554599c71dd4eab21f7d2f4a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 14:43:00 +0100 Subject: [PATCH 108/111] Small fix for a bad block transfer in Soul Calibur --- GPU/Common/FramebufferCommon.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 1f4eb712b1..d0c9efdc27 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -2000,6 +2000,10 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int } void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) { + // Clamp to width. Sometimes block transfers can cause this to hit. + if (x + w >= vfb->width) { + w = vfb->width - x; + } if (vfb) { // We'll pseudo-blit framebuffers here to get a resized version of vfb. OptimizeDownloadRange(vfb, x, y, w, h); From 5018e400f26ce3f62a25007974065197be7f0d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 14:49:50 +0100 Subject: [PATCH 109/111] Remove some code duplication --- CMakeLists.txt | 2 ++ GPU/Common/StencilCommon.cpp | 36 ++++++++++++++++++++++++++++++ GPU/Common/StencilCommon.h | 7 ++++++ GPU/D3D11/StencilBufferD3D11.cpp | 34 +--------------------------- GPU/Directx9/StencilBufferDX9.cpp | 35 +---------------------------- GPU/GLES/StencilBufferGLES.cpp | 35 +---------------------------- GPU/GPU.vcxproj | 3 +++ GPU/GPU.vcxproj.filters | 9 ++++++++ GPU/Source.cpp | 0 GPU/Vulkan/StencilBufferVulkan.cpp | 34 +--------------------------- android/jni/Android.mk | 1 + 11 files changed, 62 insertions(+), 134 deletions(-) create mode 100644 GPU/Common/StencilCommon.cpp create mode 100644 GPU/Common/StencilCommon.h create mode 100644 GPU/Source.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a1fe81d564..4e8992b23f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1267,6 +1267,8 @@ set(GPU_SOURCES GPU/Common/ShaderCommon.h GPU/Common/SplineCommon.cpp GPU/Common/SplineCommon.h + GPU/Common/StencilCommon.cpp + GPU/Common/StencilCommon.h GPU/Common/SoftwareTransformCommon.cpp GPU/Common/SoftwareTransformCommon.h GPU/Common/VertexDecoderCommon.cpp diff --git a/GPU/Common/StencilCommon.cpp b/GPU/Common/StencilCommon.cpp new file mode 100644 index 0000000000..b38faffb47 --- /dev/null +++ b/GPU/Common/StencilCommon.cpp @@ -0,0 +1,36 @@ +#pragma once + +#include "GPU/Common/StencilCommon.h" + +u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + + for (u32 i = 0; i < numPixels / 2; ++i) { + if (ptr[i] & 0x80008000) { + return 1; + } + } + return 0; +} + +u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + u32 bits = 0; + + for (u32 i = 0; i < numPixels / 2; ++i) { + bits |= ptr[i]; + } + + return ((bits >> 12) & 0xF) | (bits >> 28); +} + +u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { + const u32 *ptr = (const u32 *)ptr8; + u32 bits = 0; + + for (u32 i = 0; i < numPixels; ++i) { + bits |= ptr[i]; + } + + return bits >> 24; +} diff --git a/GPU/Common/StencilCommon.h b/GPU/Common/StencilCommon.h new file mode 100644 index 0000000000..ee26b4412e --- /dev/null +++ b/GPU/Common/StencilCommon.h @@ -0,0 +1,7 @@ +#pragma once + +#include "Common/CommonTypes.h" + +u8 StencilBits8888(const u8 *ptr8, u32 numPixels); +u8 StencilBits4444(const u8 *ptr8, u32 numPixels); +u8 StencilBits5551(const u8 *ptr8, u32 numPixels); diff --git a/GPU/D3D11/StencilBufferD3D11.cpp b/GPU/D3D11/StencilBufferD3D11.cpp index 539a9eaeb5..c8825d121b 100644 --- a/GPU/D3D11/StencilBufferD3D11.cpp +++ b/GPU/D3D11/StencilBufferD3D11.cpp @@ -21,6 +21,7 @@ #include "ext/native/thin3d/thin3d.h" #include "Core/Reporting.h" +#include "GPU/Common/StencilCommon.h" #include "GPU/D3D11/FramebufferManagerD3D11.h" #include "GPU/D3D11/FragmentShaderGeneratorD3D11.h" #include "GPU/D3D11/ShaderManagerD3D11.h" @@ -71,39 +72,6 @@ VS_OUT main(VS_IN In) { } )"; -static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - - for (u32 i = 0; i < numPixels / 2; ++i) { - if (ptr[i] & 0x80008000) { - return 1; - } - } - return 0; -} - -static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels / 2; ++i) { - bits |= ptr[i]; - } - - return ((bits >> 12) & 0xF) | (bits >> 28); -} - -static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels; ++i) { - bits |= ptr[i]; - } - - return bits >> 24; -} - // TODO : If SV_StencilRef is available (D3D11.3) then this can be done in a single pass. bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZero) { if (!MayIntersectFramebuffer(addr)) { diff --git a/GPU/Directx9/StencilBufferDX9.cpp b/GPU/Directx9/StencilBufferDX9.cpp index b667abe58c..8d8e91b610 100644 --- a/GPU/Directx9/StencilBufferDX9.cpp +++ b/GPU/Directx9/StencilBufferDX9.cpp @@ -22,6 +22,7 @@ #include "gfx/d3d9_state.h" #include "ext/native/thin3d/thin3d.h" #include "Core/Reporting.h" +#include "GPU/Common/StencilCommon.h" #include "GPU/Directx9/FramebufferDX9.h" #include "GPU/Directx9/PixelShaderGeneratorDX9.h" #include "GPU/Directx9/ShaderManagerDX9.h" @@ -63,40 +64,6 @@ static const char *stencil_vs = " return Out;\n" "}\n"; -static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - - for (u32 i = 0; i < numPixels / 2; ++i) { - if (ptr[i] & 0x80008000) { - return 1; - } - } - - return 0; -} - -static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels / 2; ++i) { - bits |= ptr[i]; - } - - return ((bits >> 12) & 0xF) | (bits >> 28); -} - -static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels; ++i) { - bits |= ptr[i]; - } - - return bits >> 24; -} - bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZero) { if (!MayIntersectFramebuffer(addr)) { return false; diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index d570214917..de04a7c207 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -18,6 +18,7 @@ #include "gfx_es2/glsl_program.h" #include "Core/Reporting.h" #include "ext/native/gfx/GLStateCache.h" +#include "GPU/Common/StencilCommon.h" #include "GPU/GLES/FramebufferManagerGLES.h" #include "GPU/GLES/ShaderManagerGLES.h" #include "GPU/GLES/TextureCacheGLES.h" @@ -59,40 +60,6 @@ static const char *stencil_vs = " gl_Position = a_position;\n" "}\n"; -static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - - for (u32 i = 0; i < numPixels / 2; ++i) { - if (ptr[i] & 0x80008000) { - return 1; - } - } - - return 0; -} - -static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels / 2; ++i) { - bits |= ptr[i]; - } - - return ((bits >> 12) & 0xF) | (bits >> 28); -} - -static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels; ++i) { - bits |= ptr[i]; - } - - return bits >> 24; -} - bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZero) { if (!MayIntersectFramebuffer(addr)) { return false; diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 30c0bd2152..9c68a6fee3 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -196,6 +196,7 @@ + true true @@ -282,6 +283,7 @@ + true true @@ -358,6 +360,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index ca0ce92bff..6e85d8e48f 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -265,6 +265,9 @@ Debugger + + Common + @@ -528,5 +531,11 @@ Vulkan + + Common + + + Common + \ No newline at end of file diff --git a/GPU/Source.cpp b/GPU/Source.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp index 313c8f309e..2958ba6518 100644 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -20,6 +20,7 @@ #include "ext/native/thin3d/thin3d.h" #include "ext/native/thin3d/VulkanRenderManager.h" #include "Core/Reporting.h" +#include "GPU/Common/StencilCommon.h" #include "GPU/Vulkan/FramebufferVulkan.h" #include "GPU/Vulkan/FragmentShaderGeneratorVulkan.h" #include "GPU/Vulkan/ShaderManagerVulkan.h" @@ -65,39 +66,6 @@ void main() { } )"; -static u8 StencilBits5551(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - - for (u32 i = 0; i < numPixels / 2; ++i) { - if (ptr[i] & 0x80008000) { - return 1; - } - } - return 0; -} - -static u8 StencilBits4444(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels / 2; ++i) { - bits |= ptr[i]; - } - - return ((bits >> 12) & 0xF) | (bits >> 28); -} - -static u8 StencilBits8888(const u8 *ptr8, u32 numPixels) { - const u32 *ptr = (const u32 *)ptr8; - u32 bits = 0; - - for (u32 i = 0; i < numPixels; ++i) { - bits |= ptr[i]; - } - - return bits >> 24; -} - // In Vulkan we should be able to simply copy the stencil data directly to a stencil buffer without // messing about with bitplane textures and the like. Or actually, maybe not... Let's start with // the traditional approach. diff --git a/android/jni/Android.mk b/android/jni/Android.mk index d5667771e3..f0f3c0ab12 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -223,6 +223,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Common/TextureCacheCommon.cpp.arm \ $(SRC)/GPU/Common/TextureScalerCommon.cpp.arm \ $(SRC)/GPU/Common/ShaderCommon.cpp \ + $(SRC)/GPU/Common/StencilCommon.cpp \ $(SRC)/GPU/Common/SplineCommon.cpp.arm \ $(SRC)/GPU/Common/DrawEngineCommon.cpp.arm \ $(SRC)/GPU/Common/TransformCommon.cpp.arm \ From 0e5b398c9d3e6a645f607a9fe47282012959c13f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 21:38:39 +0100 Subject: [PATCH 110/111] Minor optimization. --- Common/Hashmaps.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Common/Hashmaps.h b/Common/Hashmaps.h index 687b5c6994..f525553f7e 100644 --- a/Common/Hashmaps.h +++ b/Common/Hashmaps.h @@ -127,9 +127,7 @@ public: } void Clear() { - for (int i = 0; i < state.size(); i++) { - state[i] = BucketState::FREE; - } + memset(state.data(), (int)BucketState::FREE, state.size()); } void Rebuild() { @@ -266,9 +264,7 @@ public: } void Clear() { - for (int i = 0; i < state.size(); i++) { - state[i] = BucketState::FREE; - } + memset(state.data(), (int)BucketState::FREE, state.size()); } // Gets rid of REMOVED tombstones, making lookups somewhat more efficient. From 8d0209c7e46b3662bcedd907ca38b38b70827581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 1 Nov 2017 21:42:19 +0100 Subject: [PATCH 111/111] Stop leaking framebuffers on shutdown. Fix other shutdown issues. --- GPU/Common/FramebufferCommon.cpp | 16 ++++++++++++++++ UI/EmuScreen.cpp | 2 ++ ext/native/thin3d/VulkanRenderManager.cpp | 8 ++++++++ ext/native/thin3d/VulkanRenderManager.h | 3 +++ ext/native/thin3d/thin3d.h | 1 + ext/native/thin3d/thin3d_vulkan.cpp | 5 +++++ 6 files changed, 35 insertions(+) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index d0c9efdc27..a684bd68f9 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -118,6 +118,22 @@ FramebufferManagerCommon::FramebufferManagerCommon(Draw::DrawContext *draw) } FramebufferManagerCommon::~FramebufferManagerCommon() { + DecimateFBOs(); + for (auto vfb : vfbs_) { + DestroyFramebuf(vfb); + } + vfbs_.clear(); + + for (auto &tempFB : tempFBOs_) { + tempFB.second.fbo->Release(); + } + tempFBOs_.clear(); + + // Do the same for ReadFramebuffersToMemory's VFBs + for (auto vfb : bvfbs_) { + DestroyFramebuf(vfb); + } + bvfbs_.clear(); } void FramebufferManagerCommon::Init() { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 4a22e4ae18..39a1c3fb87 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -984,6 +984,8 @@ void EmuScreen::postRender() { Draw::DrawContext *draw = screenManager()->getDrawContext(); if (!draw) return; + if (invalid_) + draw->WipeQueue(); draw->EndFrame(); } diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 9b05e383cc..1d67e3fe49 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -554,6 +554,14 @@ void VulkanRenderManager::Finish() { vulkan_->EndFrame(); } +void VulkanRenderManager::Wipe() { + int curFrame = vulkan_->GetCurFrame(); + for (auto iter : steps_) { + delete iter; + } + steps_.clear(); +} + // Can be called multiple times with no bad side effects. This is so that we can either begin a frame the normal way, // or stop it in the middle for a synchronous readback, then start over again mostly normally but without repeating // the backbuffer image acquisition. diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index e808416d9c..44d34c6cc1 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -83,6 +83,9 @@ public: void Finish(); void Run(int frame); + // Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown. + void Wipe(); + void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment); void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride); diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index eb316e66e8..bd1a25d99a 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -609,6 +609,7 @@ public: // Frame management (for the purposes of sync and resource management, necessary with modern APIs). Default implementations here. virtual void BeginFrame() {} virtual void EndFrame() {} + virtual void WipeQueue() {} // This should be avoided as much as possible, in favor of clearing when binding a render target, which is native // on Vulkan. diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 3ed89d9d78..4e6eff5ddd 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -418,6 +418,7 @@ public: void BeginFrame() override; void EndFrame() override; + void WipeQueue() override; void FlushState() override { } @@ -766,6 +767,10 @@ void VKContext::EndFrame() { push_ = nullptr; } +void VKContext::WipeQueue() { + renderManager_.Wipe(); +} + VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { DescriptorSetKey key;