2016-01-03 23:09:37 +01:00
// Copyright (c) 2015- 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/.
2016-01-10 14:24:10 +01:00
# include <set>
# include <algorithm>
# include "profiler/profiler.h"
2019-06-21 00:53:51 +02:00
# include "base/display.h"
2016-01-10 14:24:10 +01:00
# include "math/lin/matrix4x4.h"
2017-02-12 18:29:58 +01:00
# include "math/dataconv.h"
2017-02-04 18:46:12 +01:00
# include "ext/native/thin3d/thin3d.h"
2016-01-10 14:24:10 +01:00
2016-02-21 20:21:24 +01:00
# include "Common/Vulkan/VulkanContext.h"
2016-03-28 22:14:04 +02:00
# include "Common/Vulkan/VulkanMemory.h"
# include "Common/Vulkan/VulkanImage.h"
2017-08-22 16:28:35 +02:00
# include "thin3d/VulkanRenderManager.h"
2016-01-10 14:24:10 +01:00
# include "Common/ColorConv.h"
# include "Core/MemMap.h"
# include "Core/Config.h"
2018-06-16 18:42:31 -07:00
# include "Core/ConfigValues.h"
2016-01-10 14:24:10 +01:00
# include "Core/System.h"
# include "Core/Reporting.h"
# include "Core/HLE/sceDisplay.h"
# include "GPU/ge_constants.h"
2020-05-10 17:14:20 -07:00
# include "GPU/GPUInterface.h"
2016-01-10 14:24:10 +01:00
# include "GPU/GPUState.h"
# include "GPU/Common/TextureDecoder.h"
# include "GPU/Common/FramebufferCommon.h"
# include "GPU/Debugger/Stepping.h"
2016-01-03 23:09:37 +01:00
# include "GPU/Vulkan/FramebufferVulkan.h"
2016-01-05 22:55:47 +01:00
# include "GPU/Vulkan/DrawEngineVulkan.h"
2016-01-10 14:24:10 +01:00
# include "GPU/Vulkan/TextureCacheVulkan.h"
# include "GPU/Vulkan/ShaderManagerVulkan.h"
2016-03-17 11:56:43 +01:00
# include "GPU/Vulkan/VulkanUtil.h"
2016-01-10 14:24:10 +01:00
2018-05-13 20:35:10 +02:00
static const char tex_fs [ ] = R " (#version 450
2016-03-28 22:14:04 +02:00
# extension GL_ARB_separate_shader_objects : enable
# extension GL_ARB_shading_language_420pack : enable
layout ( binding = 0 ) uniform sampler2D sampler0 ;
layout ( location = 0 ) in vec2 v_texcoord0 ;
layout ( location = 0 ) out vec4 fragColor ;
void main ( ) {
fragColor = texture ( sampler0 , v_texcoord0 ) ;
}
) " ;
2016-01-05 21:18:43 +01:00
2018-05-13 20:35:10 +02:00
static const char tex_vs [ ] = R " (#version 450
2016-03-28 22:14:04 +02:00
# extension GL_ARB_separate_shader_objects : enable
# extension GL_ARB_shading_language_420pack : enable
2016-03-30 21:17:20 +02:00
layout ( location = 0 ) in vec3 a_position ;
2016-03-28 22:14:04 +02:00
layout ( location = 1 ) in vec2 a_texcoord0 ;
layout ( location = 0 ) out vec2 v_texcoord0 ;
2016-05-14 16:29:40 +02:00
out gl_PerVertex { vec4 gl_Position ; } ;
2016-03-28 22:14:04 +02:00
void main ( ) {
v_texcoord0 = a_texcoord0 ;
2016-03-30 21:17:20 +02:00
gl_Position = vec4 ( a_position , 1.0 ) ;
2016-03-28 22:14:04 +02:00
}
) " ;
2016-01-10 14:24:10 +01:00
2017-02-05 19:51:50 +01:00
FramebufferManagerVulkan : : FramebufferManagerVulkan ( Draw : : DrawContext * draw , VulkanContext * vulkan ) :
FramebufferManagerCommon ( draw ) ,
2018-06-03 08:08:18 -07:00
vulkan_ ( vulkan ) {
2020-05-10 16:53:15 -07:00
presentation_ - > SetLanguage ( GLSL_VULKAN ) ;
2016-03-22 00:12:41 +01:00
2020-05-18 23:06:14 -07:00
InitDeviceObjects ( ) ;
2017-10-25 20:28:12 +02:00
// After a blit we do need to rebind for the VulkanRenderManager to know what to do.
needGLESRebinds_ = true ;
2016-10-09 11:09:30 -07:00
}
FramebufferManagerVulkan : : ~ FramebufferManagerVulkan ( ) {
2020-05-06 22:21:22 +02:00
DeviceLost ( ) ;
2016-10-09 11:09:30 -07:00
}
2017-02-06 12:02:30 +01:00
void FramebufferManagerVulkan : : SetTextureCache ( TextureCacheVulkan * tc ) {
textureCacheVulkan_ = tc ;
textureCache_ = tc ;
}
2017-02-15 18:32:44 +01:00
void FramebufferManagerVulkan : : SetShaderManager ( ShaderManagerVulkan * sm ) {
shaderManagerVulkan_ = sm ;
shaderManager_ = sm ;
}
2017-10-18 12:26:02 +02:00
void FramebufferManagerVulkan : : SetDrawEngine ( DrawEngineVulkan * td ) {
drawEngineVulkan_ = td ;
drawEngine_ = td ;
}
2016-10-09 11:09:30 -07:00
void FramebufferManagerVulkan : : InitDeviceObjects ( ) {
2016-03-28 22:14:04 +02:00
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 ) ;
2016-03-30 23:26:16 +02:00
2016-03-28 22:14:04 +02:00
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 ;
samp . addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE ;
samp . magFilter = VK_FILTER_NEAREST ;
samp . minFilter = VK_FILTER_NEAREST ;
VkResult res = vkCreateSampler ( vulkan_ - > GetDevice ( ) , & samp , nullptr , & nearestSampler_ ) ;
assert ( res = = VK_SUCCESS ) ;
samp . magFilter = VK_FILTER_LINEAR ;
samp . minFilter = VK_FILTER_LINEAR ;
res = vkCreateSampler ( vulkan_ - > GetDevice ( ) , & samp , nullptr , & linearSampler_ ) ;
assert ( res = = VK_SUCCESS ) ;
2016-01-03 23:09:37 +01:00
}
2016-01-05 21:18:43 +01:00
2016-10-09 11:09:30 -07:00
void FramebufferManagerVulkan : : DestroyDeviceObjects ( ) {
2019-09-14 11:22:16 -07:00
if ( fsBasicTex_ ! = VK_NULL_HANDLE ) {
vulkan2D_ - > PurgeFragmentShader ( fsBasicTex_ ) ;
2016-10-09 11:09:30 -07:00
vulkan_ - > Delete ( ) . QueueDeleteShaderModule ( fsBasicTex_ ) ;
2019-09-14 11:22:16 -07:00
}
if ( vsBasicTex_ ! = VK_NULL_HANDLE ) {
vulkan2D_ - > PurgeVertexShader ( vsBasicTex_ ) ;
2016-10-09 11:09:30 -07:00
vulkan_ - > Delete ( ) . QueueDeleteShaderModule ( vsBasicTex_ ) ;
2019-09-14 11:22:16 -07:00
}
if ( stencilFs_ ! = VK_NULL_HANDLE ) {
vulkan2D_ - > PurgeFragmentShader ( stencilFs_ ) ;
2017-11-01 14:18:39 +01:00
vulkan_ - > Delete ( ) . QueueDeleteShaderModule ( stencilFs_ ) ;
2019-09-14 11:22:16 -07:00
}
if ( stencilVs_ ! = VK_NULL_HANDLE ) {
vulkan2D_ - > PurgeVertexShader ( stencilVs_ ) ;
2017-11-01 14:18:39 +01:00
vulkan_ - > Delete ( ) . QueueDeleteShaderModule ( stencilVs_ ) ;
2019-09-14 11:22:16 -07:00
}
2016-03-28 22:14:04 +02:00
2016-10-09 11:09:30 -07:00
if ( linearSampler_ ! = VK_NULL_HANDLE )
vulkan_ - > Delete ( ) . QueueDeleteSampler ( linearSampler_ ) ;
if ( nearestSampler_ ! = VK_NULL_HANDLE )
vulkan_ - > Delete ( ) . QueueDeleteSampler ( nearestSampler_ ) ;
2016-03-22 00:12:41 +01:00
}
2016-03-30 23:26:16 +02:00
void FramebufferManagerVulkan : : NotifyClear ( bool clearColor , bool clearAlpha , bool clearDepth , uint32_t color , float depth ) {
2017-10-30 12:05:08 +01:00
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 ;
2016-03-30 23:26:16 +02:00
2017-11-18 00:33:20 +01:00
// Note that since the alpha channel and the stencil channel are shared on the PSP,
// when we clear alpha, we also clear stencil to the same value.
draw_ - > Clear ( mask , color , depth , color > > 24 ) ;
2017-10-30 12:05:08 +01:00
if ( clearColor | | clearAlpha ) {
SetColorUpdated ( gstate_c . skipDrawReason ) ;
}
if ( clearDepth ) {
SetDepthUpdated ( ) ;
}
2016-03-22 00:12:41 +01:00
}
2017-05-31 23:24:56 -07:00
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 ) {
2016-01-10 14:24:10 +01:00
float texCoords [ 8 ] = {
u0 , v0 ,
u1 , v0 ,
u1 , v1 ,
u0 , v1 ,
} ;
if ( uvRotation ! = ROTATION_LOCKED_HORIZONTAL ) {
float temp [ 8 ] ;
int rotation = 0 ;
switch ( uvRotation ) {
case ROTATION_LOCKED_HORIZONTAL180 : rotation = 4 ; break ;
case ROTATION_LOCKED_VERTICAL : rotation = 2 ; break ;
case ROTATION_LOCKED_VERTICAL180 : rotation = 6 ; break ;
}
for ( int i = 0 ; i < 8 ; i + + ) {
temp [ i ] = texCoords [ ( i + rotation ) & 7 ] ;
}
memcpy ( texCoords , temp , sizeof ( temp ) ) ;
}
2016-03-28 22:14:04 +02:00
Vulkan2D : : Vertex vtx [ 4 ] = {
2017-05-23 21:56:48 +02:00
{ x , y , 0 , texCoords [ 0 ] , texCoords [ 1 ] } ,
{ x + w , y , 0 , texCoords [ 2 ] , texCoords [ 3 ] } ,
{ x , y + h , 0 , texCoords [ 6 ] , texCoords [ 7 ] } ,
{ x + w , y + h , 0 , texCoords [ 4 ] , texCoords [ 5 ] } ,
2016-01-10 14:24:10 +01:00
} ;
float invDestW = 1.0f / ( destW * 0.5f ) ;
float invDestH = 1.0f / ( destH * 0.5f ) ;
for ( int i = 0 ; i < 4 ; i + + ) {
2016-03-28 22:14:04 +02:00
vtx [ i ] . x = vtx [ i ] . x * invDestW - 1.0f ;
vtx [ i ] . y = vtx [ i ] . y * invDestH - 1.0f ;
2016-01-10 14:24:10 +01:00
}
2019-06-21 14:00:02 +02:00
if ( ( flags & DRAWTEX_TO_BACKBUFFER ) & & g_display_rotation ! = DisplayRotation : : ROTATE_0 ) {
2019-06-21 00:53:51 +02:00
for ( int i = 0 ; i < 4 ; i + + ) {
2019-10-22 22:58:10 +02:00
Lin : : Vec3 v ( vtx [ i ] . x , vtx [ i ] . y , 0.0f ) ;
2019-08-04 18:00:33 +02:00
// backwards notation, should fix that...
2019-06-21 00:53:51 +02:00
v = v * g_display_rot_matrix ;
vtx [ i ] . x = v . x ;
vtx [ i ] . y = v . y ;
}
}
2017-05-21 23:13:53 +02:00
draw_ - > FlushState ( ) ;
// TODO: Should probably use draw_ directly and not go low level
2017-08-19 17:32:10 +02:00
VulkanRenderManager * renderManager = ( VulkanRenderManager * ) draw_ - > GetNativeObject ( Draw : : NativeObject : : RENDER_MANAGER ) ;
2016-03-30 21:17:20 +02:00
2020-05-11 09:47:26 -07:00
VkImageView view = ( VkImageView ) draw_ - > GetNativeObject ( Draw : : NativeObject : : BOUND_TEXTURE0_IMAGEVIEW ) ;
2017-10-30 12:05:08 +01:00
VkDescriptorSet descSet = vulkan2D_ - > GetDescriptorSet ( view , ( flags & DRAWTEX_LINEAR ) ? linearSampler_ : nearestSampler_ , VK_NULL_HANDLE , VK_NULL_HANDLE ) ;
2016-03-28 22:14:04 +02:00
VkBuffer vbuffer ;
2017-10-30 12:05:08 +01:00
VkDeviceSize offset = push_ - > Push ( vtx , sizeof ( vtx ) , & vbuffer ) ;
2017-10-23 14:53:35 +02:00
renderManager - > BindPipeline ( cur2DPipeline_ ) ;
2017-10-30 12:05:08 +01:00
renderManager - > Draw ( vulkan2D_ - > GetPipelineLayout ( ) , descSet , 0 , nullptr , vbuffer , offset , 4 ) ;
2016-01-10 14:24:10 +01:00
}
2017-02-15 16:01:59 +01:00
void FramebufferManagerVulkan : : Bind2DShader ( ) {
2017-05-21 23:13:53 +02:00
VkRenderPass rp = ( VkRenderPass ) draw_ - > GetNativeObject ( Draw : : NativeObject : : COMPATIBLE_RENDERPASS ) ;
2017-10-30 12:05:08 +01:00
cur2DPipeline_ = vulkan2D_ - > GetPipeline ( rp , vsBasicTex_ , fsBasicTex_ ) ;
2017-02-15 16:01:59 +01:00
}
2016-01-10 14:24:10 +01:00
int FramebufferManagerVulkan : : GetLineWidth ( ) {
if ( g_Config . iInternalResolution = = 0 ) {
return std : : max ( 1 , ( int ) ( renderWidth_ / 480 ) ) ;
} else {
return g_Config . iInternalResolution ;
}
}
2017-05-23 23:10:35 +02:00
// This also binds vfb as the current render target.
2016-01-10 14:24:10 +01:00
void FramebufferManagerVulkan : : ReformatFramebufferFrom ( VirtualFramebuffer * vfb , GEBufferFormat old ) {
2017-02-04 18:46:12 +01:00
if ( ! useBufferedRendering_ | | ! vfb - > fbo ) {
2016-01-10 14:24:10 +01:00
return ;
}
2016-03-28 19:57:42 +02:00
2016-01-10 14:24:10 +01:00
// Technically, we should at this point re-interpret the bytes of the old format to the new.
// That might get tricky, and could cause unnecessary slowness in some games.
// For now, we just clear alpha/stencil from 565, which fixes shadow issues in Kingdom Hearts.
2018-08-30 21:00:21 -07:00
// (it uses 565 to write zeros to the buffer, then 4444 to actually render the shadow.)
2016-01-10 14:24:10 +01:00
//
// The best way to do this may ultimately be to create a new FBO (combine with any resize?)
// and blit with a shader to that, then replace the FBO on vfb. Stencil would still be complex
// to exactly reproduce in 4444 and 8888 formats.
if ( old = = GE_FORMAT_565 ) {
2020-05-21 11:54:48 +02:00
// We have to bind here instead of clear, since it can be that no framebuffer is bound.
// The backend can sometimes directly optimize it to a clear.
draw_ - > BindFramebufferAsRenderTarget ( vfb - > fbo , { Draw : : RPAction : : CLEAR , Draw : : RPAction : : KEEP , Draw : : RPAction : : CLEAR } , " ReformatFramebuffer " ) ;
// draw_->Clear(Draw::FBChannel::FB_COLOR_BIT | Draw::FBChannel::FB_STENCIL_BIT, 0, 0.0f, 0);
// Need to dirty anything that has command buffer dynamic state, in case we started a new pass above.
// Should find a way to feed that information back, maybe... Or simply correct the issue in the rendermanager.
gstate_c . Dirty ( DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE ) ;
2016-01-10 14:24:10 +01:00
}
}
2017-05-23 23:10:35 +02:00
// Except for a missing rebind and silly scissor enables, identical copy of the same function in GPU_GLES - tricky parts are in thin3d.
2016-01-10 14:24:10 +01:00
void FramebufferManagerVulkan : : BlitFramebufferDepth ( VirtualFramebuffer * src , VirtualFramebuffer * dst ) {
2017-05-23 23:10:35 +02:00
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 ;
2017-10-25 21:56:39 +02:00
if ( matchingDepthBuffer & & matchingRenderSize & & matchingSize ) {
// TODO: Currently, this copies depth AND stencil, which is a problem. See #9740.
2020-05-21 11:24:05 +02:00
draw_ - > CopyFramebufferImage ( src - > fbo , 0 , 0 , 0 , 0 , dst - > fbo , 0 , 0 , 0 , 0 , src - > renderWidth , src - > renderHeight , 1 , Draw : : FB_DEPTH_BIT , " BlitFramebufferDepth " ) ;
2017-12-24 11:52:15 -08:00
dst - > last_frame_depth_updated = gpuStats . numFlips ;
2017-05-23 23:10:35 +02:00
} else if ( matchingDepthBuffer & & matchingSize ) {
2017-10-25 21:56:39 +02:00
/*
2017-05-23 23:10:35 +02:00
int w = std : : min ( src - > renderWidth , dst - > renderWidth ) ;
int h = std : : min ( src - > renderHeight , dst - > renderHeight ) ;
2017-06-01 20:58:33 -07:00
draw_ - > BlitFramebuffer ( src - > fbo , 0 , 0 , w , h , dst - > fbo , 0 , 0 , w , h , Draw : : FB_DEPTH_BIT , Draw : : FB_BLIT_NEAREST ) ;
2017-10-25 21:56:39 +02:00
*/
2016-03-28 19:57:42 +02:00
}
2016-01-10 14:24:10 +01:00
}
2017-05-22 10:42:40 +02:00
VkImageView FramebufferManagerVulkan : : BindFramebufferAsColorTexture ( int stage , VirtualFramebuffer * framebuffer , int flags ) {
if ( ! framebuffer - > fbo | | ! useBufferedRendering_ ) {
gstate_c . skipDrawReason | = SKIPDRAW_BAD_FB_TEXTURE ;
return VK_NULL_HANDLE ;
}
// 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 ;
2019-02-08 15:02:31 +01:00
if ( GPUStepping : : IsStepping ( ) ) {
2017-05-22 10:42:40 +02:00
skipCopy = true ;
}
// Currently rendering to this framebuffer. Need to make a copy.
if ( ! skipCopy & & framebuffer = = currentRenderVfb_ ) {
// TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size.
2018-05-06 08:57:44 -07:00
Draw : : Framebuffer * renderCopy = GetTempFBO ( TempFBO : : COPY , framebuffer - > renderWidth , framebuffer - > renderHeight , ( Draw : : FBColorDepth ) framebuffer - > colorDepth ) ;
2017-05-22 10:42:40 +02:00
if ( renderCopy ) {
VirtualFramebuffer copyInfo = * framebuffer ;
copyInfo . fbo = renderCopy ;
CopyFramebufferForColorTexture ( & copyInfo , framebuffer , flags ) ;
2020-06-02 09:51:38 +02:00
RebindFramebuffer ( " RebindFramebuffer - BindFramebufferAsColorTexture " ) ;
2017-05-22 10:42:40 +02:00
draw_ - > BindFramebufferAsTexture ( renderCopy , stage , Draw : : FB_COLOR_BIT , 0 ) ;
} else {
draw_ - > BindFramebufferAsTexture ( framebuffer - > fbo , stage , Draw : : FB_COLOR_BIT , 0 ) ;
}
2017-10-31 12:02:10 +01:00
return ( VkImageView ) draw_ - > GetNativeObject ( Draw : : NativeObject : : BOUND_TEXTURE0_IMAGEVIEW ) ;
2018-11-24 10:19:10 -08:00
} else if ( framebuffer ! = currentRenderVfb_ | | ( flags & BINDFBCOLOR_FORCE_SELF ) ! = 0 ) {
2017-05-22 10:42:40 +02:00
draw_ - > BindFramebufferAsTexture ( framebuffer - > fbo , stage , Draw : : FB_COLOR_BIT , 0 ) ;
2017-10-31 12:02:10 +01:00
return ( VkImageView ) draw_ - > GetNativeObject ( Draw : : NativeObject : : BOUND_TEXTURE0_IMAGEVIEW ) ;
2017-05-22 10:42:40 +02:00
} else {
2017-11-30 15:40:55 -08:00
ERROR_LOG_REPORT_ONCE ( vulkanSelfTexture , G3D , " Attempting to texture from target (src=%08x / target=%08x / flags=%d) " , framebuffer - > fb_address , currentRenderVfb_ - > fb_address , flags ) ;
2017-05-23 11:12:10 +02:00
// To do this safely in Vulkan, we need to use input attachments.
2017-05-22 10:42:40 +02:00
return VK_NULL_HANDLE ;
}
}
2016-01-10 14:24:10 +01:00
void FramebufferManagerVulkan : : UpdateDownloadTempBuffer ( VirtualFramebuffer * nvfb ) {
2017-10-09 12:43:56 +02:00
// Nothing to do here.
2016-01-10 14:24:10 +01:00
}
void FramebufferManagerVulkan : : BlitFramebuffer ( VirtualFramebuffer * dst , int dstX , int dstY , VirtualFramebuffer * src , int srcX , int srcY , int w , int h , int bpp ) {
if ( ! dst - > fbo | | ! src - > fbo | | ! useBufferedRendering_ ) {
// This can happen if they recently switched from non-buffered.
2020-05-20 09:47:11 +02:00
if ( useBufferedRendering_ ) {
2020-05-21 11:24:05 +02:00
draw_ - > BindFramebufferAsRenderTarget ( nullptr , { Draw : : RPAction : : KEEP , Draw : : RPAction : : KEEP , Draw : : RPAction : : KEEP } , " BlitFramebuffer_Fail " ) ;
2020-05-20 09:47:11 +02:00
gstate_c . Dirty ( DIRTY_VIEWPORTSCISSOR_STATE ) ;
}
2016-01-10 14:24:10 +01:00
return ;
}
2017-12-03 14:49:45 +01:00
// Perform a little bit of clipping first.
// Block transfer coords are unsigned so I don't think we need to clip on the left side..
if ( dstX + w > dst - > bufferWidth ) {
w - = dstX + w - dst - > bufferWidth ;
}
if ( dstY + h > dst - > bufferHeight ) {
h - = dstY + h - dst - > bufferHeight ;
}
2018-11-05 00:27:29 +01:00
if ( srcX + w > src - > bufferWidth ) {
w - = srcX + w - src - > bufferWidth ;
}
if ( srcY + h > src - > bufferHeight ) {
h - = srcY + h - src - > bufferHeight ;
}
2020-04-26 12:44:11 +02:00
if ( w < = 0 | | h < = 0 ) {
// The whole rectangle got clipped.
2017-12-03 14:49:45 +01:00
return ;
2020-04-26 12:44:11 +02:00
}
2017-12-03 14:49:45 +01:00
2017-05-24 00:45:15 +02:00
float srcXFactor = ( float ) src - > renderWidth / ( float ) src - > bufferWidth ;
float srcYFactor = ( float ) src - > renderHeight / ( float ) src - > bufferHeight ;
2016-01-10 14:24:10 +01:00
const int srcBpp = src - > format = = GE_FORMAT_8888 ? 4 : 2 ;
if ( srcBpp ! = bpp & & bpp ! = 0 ) {
srcXFactor = ( srcXFactor * bpp ) / srcBpp ;
}
int srcX1 = srcX * srcXFactor ;
int srcX2 = ( srcX + w ) * srcXFactor ;
int srcY1 = srcY * srcYFactor ;
int srcY2 = ( srcY + h ) * srcYFactor ;
2017-05-24 00:45:15 +02:00
float dstXFactor = ( float ) dst - > renderWidth / ( float ) dst - > bufferWidth ;
float dstYFactor = ( float ) dst - > renderHeight / ( float ) dst - > bufferHeight ;
2016-01-10 14:24:10 +01:00
const int dstBpp = dst - > format = = GE_FORMAT_8888 ? 4 : 2 ;
if ( dstBpp ! = bpp & & bpp ! = 0 ) {
dstXFactor = ( dstXFactor * bpp ) / dstBpp ;
}
int dstX1 = dstX * dstXFactor ;
int dstX2 = ( dstX + w ) * dstXFactor ;
int dstY1 = dstY * dstYFactor ;
int dstY2 = ( dstY + h ) * dstYFactor ;
if ( src = = dst & & srcX = = dstX & & srcY = = dstY ) {
// Let's just skip a copy where the destination is equal to the source.
WARN_LOG_REPORT_ONCE ( blitSame , G3D , " Skipped blit with equal dst and src " ) ;
return ;
}
2017-05-24 00:45:15 +02:00
// BlitFramebuffer can clip, but CopyFramebufferImage is more restricted.
2016-03-28 19:57:42 +02:00
// In case the src goes outside, we just skip the optimization in that case.
const bool sameSize = dstX2 - dstX1 = = srcX2 - srcX1 & & dstY2 - dstY1 = = srcY2 - srcY1 ;
const bool sameDepth = dst - > colorDepth = = src - > colorDepth ;
const bool srcInsideBounds = srcX2 < = src - > renderWidth & & srcY2 < = src - > renderHeight ;
const bool dstInsideBounds = dstX2 < = dst - > renderWidth & & dstY2 < = dst - > renderHeight ;
const bool xOverlap = src = = dst & & srcX2 > dstX1 & & srcX1 < dstX2 ;
const bool yOverlap = src = = dst & & srcY2 > dstY1 & & srcY1 < dstY2 ;
if ( sameSize & & sameDepth & & srcInsideBounds & & dstInsideBounds & & ! ( xOverlap & & yOverlap ) ) {
2020-05-21 11:24:05 +02:00
draw_ - > CopyFramebufferImage ( src - > fbo , 0 , srcX1 , srcY1 , 0 , dst - > fbo , 0 , dstX1 , dstY1 , 0 , dstX2 - dstX1 , dstY2 - dstY1 , 1 , Draw : : FB_COLOR_BIT , " BlitFramebuffer_Copy " ) ;
2016-01-10 14:24:10 +01:00
} else {
2020-05-21 11:24:05 +02:00
draw_ - > BlitFramebuffer ( src - > fbo , srcX1 , srcY1 , srcX2 , srcY2 , dst - > fbo , dstX1 , dstY1 , dstX2 , dstY2 , Draw : : FB_COLOR_BIT , Draw : : FB_BLIT_NEAREST , " BlitFramebuffer_Blit " ) ;
2016-01-10 14:24:10 +01:00
}
}
2016-03-22 00:12:41 +01:00
void FramebufferManagerVulkan : : BeginFrameVulkan ( ) {
BeginFrame ( ) ;
}
2016-01-10 14:24:10 +01:00
void FramebufferManagerVulkan : : EndFrame ( ) {
}
void FramebufferManagerVulkan : : DeviceLost ( ) {
2017-04-13 23:07:21 -07:00
DestroyAllFBOs ( ) ;
2016-10-09 11:09:30 -07:00
DestroyDeviceObjects ( ) ;
2020-05-09 22:06:22 -07:00
presentation_ - > DeviceLost ( ) ;
2016-10-09 11:09:30 -07:00
}
2017-11-09 16:28:22 +01:00
void FramebufferManagerVulkan : : DeviceRestore ( VulkanContext * vulkan , Draw : : DrawContext * draw ) {
2016-10-09 11:09:30 -07:00
vulkan_ = vulkan ;
2017-11-09 16:28:22 +01:00
draw_ = draw ;
2020-05-09 22:06:22 -07:00
presentation_ - > DeviceRestore ( draw ) ;
2016-10-09 11:09:30 -07:00
InitDeviceObjects ( ) ;
2016-01-10 14:24:10 +01:00
}