2020-05-17 14:53:37 -07:00
# include <unordered_map>
2019-09-18 00:16:08 +02:00
2020-10-04 23:24:14 +02:00
# include "Common/GPU/DataFormat.h"
2022-05-01 11:32:57 +02:00
# include "Common/GPU/Vulkan/VulkanQueueRunner.h"
# include "Common/GPU/Vulkan/VulkanRenderManager.h"
2022-10-12 16:21:54 +02:00
# include "Common/VR/PPSSPPVR.h"
2017-11-22 12:24:05 +01:00
# include "Common/Log.h"
2020-08-15 20:53:08 +02:00
# include "Common/TimeUtil.h"
2017-10-27 22:10:36 +02:00
2021-08-21 12:39:15 +02:00
using namespace PPSSPP_VK ;
2021-08-20 11:22:57 +02:00
2023-05-16 14:34:28 +02:00
// Debug help: adb logcat -s DEBUG AndroidRuntime PPSSPPNativeActivity PPSSPP NativeGLView NativeRenderer NativeSurfaceView PowerSaveModeReceiver InputDeviceState PpssppActivity CameraHelper
2017-11-08 17:01:38 +01:00
2022-12-10 21:02:44 -08:00
static void MergeRenderAreaRectInto ( VkRect2D * dest , const VkRect2D & src ) {
2020-11-26 23:28:27 +01:00
if ( dest - > offset . x > src . offset . x ) {
dest - > extent . width + = ( dest - > offset . x - src . offset . x ) ;
dest - > offset . x = src . offset . x ;
}
if ( dest - > offset . y > src . offset . y ) {
dest - > extent . height + = ( dest - > offset . y - src . offset . y ) ;
dest - > offset . y = src . offset . y ;
}
if ( dest - > extent . width < src . extent . width ) {
dest - > extent . width = src . extent . width ;
}
if ( dest - > extent . height < src . extent . height ) {
dest - > extent . height = src . extent . height ;
}
}
2022-09-07 18:44:48 +02:00
// We need to take the "max" of the features used in the two render passes.
RenderPassType MergeRPTypes ( RenderPassType a , RenderPassType b ) {
// Either both are backbuffer type, or neither are.
2022-09-22 10:22:29 +02:00
// These can't merge with other renderpasses
2022-11-05 22:06:53 +01:00
if ( a = = RenderPassType : : BACKBUFFER | | b = = RenderPassType : : BACKBUFFER ) {
2022-09-22 10:22:29 +02:00
_dbg_assert_ ( a = = b ) ;
2022-09-07 18:44:48 +02:00
return a ;
}
2022-09-22 10:22:29 +02:00
2022-11-05 22:06:53 +01:00
_dbg_assert_ ( ( a & RenderPassType : : MULTIVIEW ) = = ( b & RenderPassType : : MULTIVIEW ) ) ;
2022-10-23 11:21:35 +02:00
2022-09-22 10:22:29 +02:00
// The rest we can just OR together to get the maximum feature set.
return ( RenderPassType ) ( ( u32 ) a | ( u32 ) b ) ;
2022-09-07 18:44:48 +02:00
}
2017-10-27 22:10:36 +02:00
void VulkanQueueRunner : : CreateDeviceObjects ( ) {
2020-08-15 12:12:57 +02:00
INFO_LOG ( G3D , " VulkanQueueRunner::CreateDeviceObjects " ) ;
2017-12-05 11:42:51 +01:00
2022-09-06 13:30:18 +02:00
RPKey key {
VKRRenderPassLoadAction : : CLEAR , VKRRenderPassLoadAction : : CLEAR , VKRRenderPassLoadAction : : CLEAR ,
VKRRenderPassStoreAction : : STORE , VKRRenderPassStoreAction : : DONT_CARE , VKRRenderPassStoreAction : : DONT_CARE ,
} ;
compatibleRenderPass_ = GetRenderPass ( key ) ;
2018-03-27 14:44:51 +02:00
2017-12-05 11:42:51 +01:00
#if 0
// Just to check whether it makes sense to split some of these. drawidx is way bigger than the others...
// We should probably just move to variable-size data in a raw buffer anyway...
VkRenderData rd ;
2020-08-15 12:12:57 +02:00
INFO_LOG ( G3D , " sizeof(pipeline): %d " , ( int ) sizeof ( rd . pipeline ) ) ;
INFO_LOG ( G3D , " sizeof(draw): %d " , ( int ) sizeof ( rd . draw ) ) ;
INFO_LOG ( G3D , " sizeof(drawidx): %d " , ( int ) sizeof ( rd . drawIndexed ) ) ;
INFO_LOG ( G3D , " sizeof(clear): %d " , ( int ) sizeof ( rd . clear ) ) ;
INFO_LOG ( G3D , " sizeof(viewport): %d " , ( int ) sizeof ( rd . viewport ) ) ;
INFO_LOG ( G3D , " sizeof(scissor): %d " , ( int ) sizeof ( rd . scissor ) ) ;
INFO_LOG ( G3D , " sizeof(blendColor): %d " , ( int ) sizeof ( rd . blendColor ) ) ;
INFO_LOG ( G3D , " sizeof(push): %d " , ( int ) sizeof ( rd . push ) ) ;
2017-12-05 11:42:51 +01:00
# endif
2017-11-12 09:53:46 +01:00
}
2017-10-27 22:10:36 +02:00
void VulkanQueueRunner : : DestroyDeviceObjects ( ) {
2020-08-15 12:12:57 +02:00
INFO_LOG ( G3D , " VulkanQueueRunner::DestroyDeviceObjects " ) ;
2023-02-05 16:59:23 +01:00
syncReadback_ . Destroy ( vulkan_ ) ;
2017-10-28 18:03:27 +02:00
2022-09-06 13:30:18 +02:00
renderPasses_ . IterateMut ( [ & ] ( const RPKey & rpkey , VKRRenderPass * rp ) {
_assert_ ( rp ) ;
2022-09-07 12:48:16 +02:00
rp - > Destroy ( vulkan_ ) ;
2022-09-06 13:30:18 +02:00
delete rp ;
2017-12-30 22:35:43 +01:00
} ) ;
renderPasses_ . Clear ( ) ;
2017-10-27 22:10:36 +02:00
}
2022-09-17 08:43:13 +02:00
bool VulkanQueueRunner : : CreateSwapchain ( VkCommandBuffer cmdInit ) {
VkResult res = vkGetSwapchainImagesKHR ( vulkan_ - > GetDevice ( ) , vulkan_ - > GetSwapchain ( ) , & swapchainImageCount_ , nullptr ) ;
_dbg_assert_ ( res = = VK_SUCCESS ) ;
VkImage * swapchainImages = new VkImage [ swapchainImageCount_ ] ;
res = vkGetSwapchainImagesKHR ( vulkan_ - > GetDevice ( ) , vulkan_ - > GetSwapchain ( ) , & swapchainImageCount_ , swapchainImages ) ;
if ( res ! = VK_SUCCESS ) {
ERROR_LOG ( G3D , " vkGetSwapchainImagesKHR failed " ) ;
delete [ ] swapchainImages ;
return false ;
}
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 ( ) ;
color_image_view . components . r = VK_COMPONENT_SWIZZLE_IDENTITY ;
color_image_view . components . g = VK_COMPONENT_SWIZZLE_IDENTITY ;
color_image_view . components . b = VK_COMPONENT_SWIZZLE_IDENTITY ;
color_image_view . components . a = VK_COMPONENT_SWIZZLE_IDENTITY ;
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 ;
2022-10-23 11:21:35 +02:00
color_image_view . subresourceRange . layerCount = 1 ; // TODO: Investigate hw-assisted stereo.
2022-09-17 08:43:13 +02:00
color_image_view . viewType = VK_IMAGE_VIEW_TYPE_2D ;
color_image_view . flags = 0 ;
color_image_view . image = sc_buffer . image ;
// We leave the images as UNDEFINED, there's no need to pre-transition them as
// the backbuffer renderpass starts out with them being auto-transitioned from UNDEFINED anyway.
// Also, turns out it's illegal to transition un-acquired images, thanks Hans-Kristian. See #11417.
res = vkCreateImageView ( vulkan_ - > GetDevice ( ) , & color_image_view , nullptr , & sc_buffer . view ) ;
2022-10-26 13:29:56 +02:00
vulkan_ - > SetDebugName ( sc_buffer . view , VK_OBJECT_TYPE_IMAGE_VIEW , " swapchain_view " ) ;
2022-09-17 08:43:13 +02:00
swapchainImages_ . push_back ( sc_buffer ) ;
_dbg_assert_ ( res = = VK_SUCCESS ) ;
}
delete [ ] swapchainImages ;
// Must be before InitBackbufferRenderPass.
if ( InitDepthStencilBuffer ( cmdInit ) ) {
InitBackbufferFramebuffers ( vulkan_ - > GetBackbufferWidth ( ) , vulkan_ - > GetBackbufferHeight ( ) ) ;
}
return true ;
}
bool VulkanQueueRunner : : InitBackbufferFramebuffers ( int width , int height ) {
VkResult res ;
// We share the same depth buffer but have multiple color buffers, see the loop below.
VkImageView attachments [ 2 ] = { VK_NULL_HANDLE , depth_ . view } ;
VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO } ;
2022-11-27 11:39:44 +01:00
fb_info . renderPass = GetCompatibleRenderPass ( ) - > Get ( vulkan_ , RenderPassType : : BACKBUFFER , VK_SAMPLE_COUNT_1_BIT ) ;
2022-09-17 08:43:13 +02:00
fb_info . attachmentCount = 2 ;
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 ] = swapchainImages_ [ i ] . view ;
res = vkCreateFramebuffer ( vulkan_ - > GetDevice ( ) , & fb_info , nullptr , & framebuffers_ [ i ] ) ;
_dbg_assert_ ( res = = VK_SUCCESS ) ;
if ( res ! = VK_SUCCESS ) {
framebuffers_ . clear ( ) ;
return false ;
}
}
return true ;
}
bool VulkanQueueRunner : : InitDepthStencilBuffer ( VkCommandBuffer cmd ) {
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 = vulkan_ - > GetBackbufferWidth ( ) ;
image_info . extent . height = vulkan_ - > GetBackbufferHeight ( ) ;
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 ;
depth_ . format = depth_format ;
VmaAllocationCreateInfo allocCreateInfo { } ;
VmaAllocationInfo allocInfo { } ;
allocCreateInfo . usage = VMA_MEMORY_USAGE_GPU_ONLY ;
VkResult res = vmaCreateImage ( vulkan_ - > Allocator ( ) , & image_info , & allocCreateInfo , & depth_ . image , & depth_ . alloc , & allocInfo ) ;
_dbg_assert_ ( res = = VK_SUCCESS ) ;
if ( res ! = VK_SUCCESS )
return false ;
vulkan_ - > SetDebugName ( depth_ . image , VK_OBJECT_TYPE_IMAGE , " BackbufferDepth " ) ;
2022-10-18 00:26:10 +02:00
TransitionImageLayout2 ( cmd , depth_ . image , 0 , 1 , 1 ,
2022-09-17 08:43:13 +02:00
aspectMask ,
VK_IMAGE_LAYOUT_UNDEFINED , VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT ,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_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_IDENTITY ;
depth_view_info . components . g = VK_COMPONENT_SWIZZLE_IDENTITY ;
depth_view_info . components . b = VK_COMPONENT_SWIZZLE_IDENTITY ;
depth_view_info . components . a = VK_COMPONENT_SWIZZLE_IDENTITY ;
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 ;
VkDevice device = vulkan_ - > GetDevice ( ) ;
res = vkCreateImageView ( device , & depth_view_info , NULL , & depth_ . view ) ;
2022-10-26 13:29:56 +02:00
vulkan_ - > SetDebugName ( depth_ . view , VK_OBJECT_TYPE_IMAGE_VIEW , " depth_stencil_backbuffer " ) ;
2022-09-17 08:43:13 +02:00
_dbg_assert_ ( res = = VK_SUCCESS ) ;
if ( res ! = VK_SUCCESS )
return false ;
return true ;
}
void VulkanQueueRunner : : DestroyBackBuffers ( ) {
for ( auto & image : swapchainImages_ ) {
vulkan_ - > Delete ( ) . QueueDeleteImageView ( image . view ) ;
}
swapchainImages_ . clear ( ) ;
if ( depth_ . view ) {
vulkan_ - > Delete ( ) . QueueDeleteImageView ( depth_ . view ) ;
}
if ( depth_ . image ) {
_dbg_assert_ ( depth_ . alloc ) ;
vulkan_ - > Delete ( ) . QueueDeleteImageAllocation ( depth_ . image , depth_ . alloc ) ;
}
depth_ = { } ;
for ( uint32_t i = 0 ; i < framebuffers_ . size ( ) ; i + + ) {
_dbg_assert_ ( framebuffers_ [ i ] ! = VK_NULL_HANDLE ) ;
vulkan_ - > Delete ( ) . QueueDeleteFramebuffer ( framebuffers_ [ i ] ) ;
}
framebuffers_ . clear ( ) ;
INFO_LOG ( G3D , " Backbuffers destroyed " ) ;
}
2022-09-07 12:48:16 +02:00
2022-09-06 13:30:18 +02:00
// Self-dependency: https://github.com/gpuweb/gpuweb/issues/442#issuecomment-547604827
// Also see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies
VKRRenderPass * VulkanQueueRunner : : GetRenderPass ( const RPKey & key ) {
auto foundPass = renderPasses_ . Get ( key ) ;
if ( foundPass ) {
return foundPass ;
}
2022-09-07 12:48:16 +02:00
VKRRenderPass * pass = new VKRRenderPass ( key ) ;
2017-12-30 22:35:43 +01:00
renderPasses_ . Insert ( key , pass ) ;
return pass ;
2017-10-27 22:10:36 +02:00
}
2017-11-04 10:08:53 +01:00
2022-02-19 20:40:27 +01:00
// Must match the subpass self-dependency declared above.
void VulkanQueueRunner : : SelfDependencyBarrier ( VKRImage & img , VkImageAspectFlags aspect , VulkanBarrier * recordBarrier ) {
if ( aspect & VK_IMAGE_ASPECT_COLOR_BIT ) {
VkAccessFlags srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ;
VkAccessFlags dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT ;
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ;
recordBarrier - > TransitionImage (
img . image ,
0 ,
1 ,
2022-10-23 11:21:35 +02:00
img . numLayers ,
2022-02-19 20:40:27 +01:00
aspect ,
VK_IMAGE_LAYOUT_GENERAL ,
VK_IMAGE_LAYOUT_GENERAL ,
srcAccessMask ,
dstAccessMask ,
srcStageMask ,
dstStageMask
) ;
} else {
_assert_msg_ ( false , " Depth self-dependencies not yet supported " ) ;
}
}
2020-08-27 16:53:26 +02:00
void VulkanQueueRunner : : PreprocessSteps ( std : : vector < VKRStep * > & steps ) {
2017-10-27 22:10:36 +02:00
// Optimizes renderpasses, then sequences them.
2017-12-05 13:05:11 +01:00
// Planned optimizations:
// * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes
2022-09-06 13:30:18 +02:00
// as early as possible in the frame (Wipeout billboards). This will require taking over more of descriptor management so we can
// substitute descriptors, alternatively using texture array layers creatively.
2017-12-05 13:05:11 +01:00
2019-08-08 14:07:53 +02:00
for ( int j = 0 ; j < ( int ) steps . size ( ) ; j + + ) {
2018-03-18 12:03:00 +01:00
if ( steps [ j ] - > stepType = = VKRStepType : : RENDER & &
2017-11-22 12:24:05 +01:00
steps [ j ] - > render . framebuffer ) {
if ( steps [ j ] - > render . finalColorLayout = = VK_IMAGE_LAYOUT_UNDEFINED ) {
steps [ j ] - > render . finalColorLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ;
}
if ( steps [ j ] - > render . finalDepthStencilLayout = = VK_IMAGE_LAYOUT_UNDEFINED ) {
steps [ j ] - > render . finalDepthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ;
}
2018-03-18 12:03:00 +01:00
}
2019-08-08 14:07:53 +02:00
}
2018-03-18 12:03:00 +01:00
2019-08-08 14:07:53 +02:00
for ( int j = 0 ; j < ( int ) steps . size ( ) - 1 ; j + + ) {
2018-05-27 21:54:07 +02:00
// Push down empty "Clear/Store" renderpasses, and merge them with the first "Load/Store" to the same framebuffer.
2018-03-16 14:57:41 +01:00
if ( steps . size ( ) > 1 & & steps [ j ] - > stepType = = VKRStepType : : RENDER & &
steps [ j ] - > render . numDraws = = 0 & &
2018-05-27 14:14:02 -07:00
steps [ j ] - > render . numReads = = 0 & &
2022-06-11 13:22:40 +02:00
steps [ j ] - > render . colorLoad = = VKRRenderPassLoadAction : : CLEAR & &
steps [ j ] - > render . stencilLoad = = VKRRenderPassLoadAction : : CLEAR & &
steps [ j ] - > render . depthLoad = = VKRRenderPassLoadAction : : CLEAR ) {
2018-03-16 14:57:41 +01:00
2019-08-13 00:22:47 +02:00
// Drop the clear step, and merge it into the next step that touches the same framebuffer.
for ( int i = j + 1 ; i < ( int ) steps . size ( ) ; i + + ) {
2018-03-16 14:57:41 +01:00
if ( steps [ i ] - > stepType = = VKRStepType : : RENDER & &
steps [ i ] - > render . framebuffer = = steps [ j ] - > render . framebuffer ) {
2022-06-11 13:22:40 +02:00
if ( steps [ i ] - > render . colorLoad ! = VKRRenderPassLoadAction : : CLEAR ) {
steps [ i ] - > render . colorLoad = VKRRenderPassLoadAction : : CLEAR ;
2018-03-16 14:57:41 +01:00
steps [ i ] - > render . clearColor = steps [ j ] - > render . clearColor ;
}
2022-06-11 13:22:40 +02:00
if ( steps [ i ] - > render . depthLoad ! = VKRRenderPassLoadAction : : CLEAR ) {
steps [ i ] - > render . depthLoad = VKRRenderPassLoadAction : : CLEAR ;
2018-03-16 14:57:41 +01:00
steps [ i ] - > render . clearDepth = steps [ j ] - > render . clearDepth ;
}
2022-06-11 13:22:40 +02:00
if ( steps [ i ] - > render . stencilLoad ! = VKRRenderPassLoadAction : : CLEAR ) {
steps [ i ] - > render . stencilLoad = VKRRenderPassLoadAction : : CLEAR ;
2018-03-16 14:57:41 +01:00
steps [ i ] - > render . clearStencil = steps [ j ] - > render . clearStencil ;
}
2020-11-26 23:28:27 +01:00
MergeRenderAreaRectInto ( & steps [ i ] - > render . renderArea , steps [ j ] - > render . renderArea ) ;
2022-09-07 18:44:48 +02:00
steps [ i ] - > render . renderPassType = MergeRPTypes ( steps [ i ] - > render . renderPassType , steps [ j ] - > render . renderPassType ) ;
2022-10-04 17:56:30 +02:00
steps [ i ] - > render . numDraws + = steps [ j ] - > render . numDraws ;
steps [ i ] - > render . numReads + = steps [ j ] - > render . numReads ;
2018-03-16 14:57:41 +01:00
// Cheaply skip the first step.
steps [ j ] - > stepType = VKRStepType : : RENDER_SKIP ;
break ;
2018-03-18 09:43:13 +01:00
} else if ( steps [ i ] - > stepType = = VKRStepType : : COPY & &
steps [ i ] - > copy . src = = steps [ j ] - > render . framebuffer ) {
// Can't eliminate the clear if a game copies from it before it's
// rendered to. However this should be rare.
2018-05-27 14:14:02 -07:00
// TODO: This should never happen when we check numReads now.
2018-03-18 09:43:13 +01:00
break ;
2018-03-16 13:35:49 +01:00
}
}
}
}
2018-04-13 17:32:36 +02:00
// Queue hacks.
2018-04-13 18:05:04 +02:00
if ( hacksEnabled_ ) {
if ( hacksEnabled_ & QUEUE_HACK_MGS2_ACID ) {
// Massive speedup.
ApplyMGSHack ( steps ) ;
}
if ( hacksEnabled_ & QUEUE_HACK_SONIC ) {
ApplySonicHack ( steps ) ;
}
2019-08-13 23:07:58 +02:00
if ( hacksEnabled_ & QUEUE_HACK_RENDERPASS_MERGE ) {
ApplyRenderPassMerge ( steps ) ;
}
2018-04-13 17:32:36 +02:00
}
2020-08-27 16:53:26 +02:00
}
2022-10-12 16:21:54 +02:00
void VulkanQueueRunner : : RunSteps ( std : : vector < VKRStep * > & steps , FrameData & frameData , FrameDataShared & frameDataShared , bool keepSteps ) {
2022-09-17 01:41:26 +02:00
QueueProfileContext * profile = frameData . profilingEnabled_ ? & frameData . profile : nullptr ;
2020-08-27 16:53:26 +02:00
if ( profile )
2020-09-24 23:52:03 +02:00
profile - > cpuStartTime = time_now_d ( ) ;
2018-04-13 17:32:36 +02:00
2020-08-08 23:18:17 +02:00
bool emitLabels = vulkan_ - > Extensions ( ) . EXT_debug_utils ;
2022-09-01 14:21:34 +02:00
2022-09-20 17:48:19 +02:00
VkCommandBuffer cmd = frameData . hasPresentCommands ? frameData . presentCmd : frameData . mainCmd ;
2022-09-23 19:39:00 +02:00
for ( size_t i = 0 ; i < steps . size ( ) ; i + + ) {
const VKRStep & step = * steps [ i ] ;
2020-05-21 08:38:48 -07:00
if ( emitLabels ) {
VkDebugUtilsLabelEXT labelInfo { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT } ;
2022-09-27 23:40:50 +02:00
char temp [ 128 ] ;
if ( step . stepType = = VKRStepType : : RENDER & & step . render . framebuffer ) {
snprintf ( temp , sizeof ( temp ) , " %s: %s " , step . tag , step . render . framebuffer - > Tag ( ) ) ;
labelInfo . pLabelName = temp ;
} else {
labelInfo . pLabelName = step . tag ;
}
2023-04-01 23:28:59 +02:00
vkCmdBeginDebugUtilsLabelEXT ( cmd , & labelInfo ) ;
2020-05-21 08:38:48 -07:00
}
2017-10-27 22:10:36 +02:00
switch ( step . stepType ) {
case VKRStepType : : RENDER :
2022-09-17 01:11:41 +02:00
if ( ! step . render . framebuffer ) {
2023-04-01 23:28:59 +02:00
if ( emitLabels ) {
vkCmdEndDebugUtilsLabelEXT ( cmd ) ;
}
2022-09-20 17:52:35 +02:00
frameData . SubmitPending ( vulkan_ , FrameSubmitType : : Pending , frameDataShared ) ;
2022-09-18 12:09:56 +02:00
// When stepping in the GE debugger, we can end up here multiple times in a "frame".
2022-09-20 17:48:19 +02:00
// So only acquire once.
2022-09-18 12:09:56 +02:00
if ( ! frameData . hasAcquired ) {
2022-09-19 18:07:50 +02:00
frameData . AcquireNextImage ( vulkan_ , frameDataShared ) ;
2022-09-17 08:43:13 +02:00
SetBackbuffer ( framebuffers_ [ frameData . curSwapchainImage ] , swapchainImages_ [ frameData . curSwapchainImage ] . image ) ;
2022-09-17 01:11:41 +02:00
}
2022-09-20 17:48:19 +02:00
2022-09-23 18:45:31 -07:00
if ( ! frameData . hasPresentCommands ) {
// A RENDER step rendering to the backbuffer is normally the last step that happens in a frame,
// unless taking a screenshot, in which case there might be a READBACK_IMAGE after it.
// This is why we have to switch cmd to presentCmd, in this case.
VkCommandBufferBeginInfo begin { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO } ;
begin . flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT ;
vkBeginCommandBuffer ( frameData . presentCmd , & begin ) ;
frameData . hasPresentCommands = true ;
}
2022-09-20 17:48:19 +02:00
cmd = frameData . presentCmd ;
2023-04-01 23:28:59 +02:00
if ( emitLabels ) {
VkDebugUtilsLabelEXT labelInfo { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT } ;
labelInfo . pLabelName = " present " ;
vkCmdBeginDebugUtilsLabelEXT ( cmd , & labelInfo ) ;
}
2022-09-17 01:11:41 +02:00
}
2022-09-20 17:48:19 +02:00
PerformRenderPass ( step , cmd ) ;
2017-10-27 22:10:36 +02:00
break ;
case VKRStepType : : COPY :
2022-09-20 17:48:19 +02:00
PerformCopy ( step , cmd ) ;
2017-10-27 22:10:36 +02:00
break ;
case VKRStepType : : BLIT :
2022-09-20 17:48:19 +02:00
PerformBlit ( step , cmd ) ;
2017-10-27 22:10:36 +02:00
break ;
case VKRStepType : : READBACK :
2023-02-05 16:59:23 +01:00
PerformReadback ( step , cmd , frameData ) ;
2017-10-27 22:10:36 +02:00
break ;
2017-11-05 18:03:53 -08:00
case VKRStepType : : READBACK_IMAGE :
2022-09-20 17:48:19 +02:00
PerformReadbackImage ( step , cmd ) ;
2017-11-05 18:03:53 -08:00
break ;
2018-03-16 13:35:49 +01:00
case VKRStepType : : RENDER_SKIP :
break ;
2017-10-27 22:10:36 +02:00
}
2019-08-21 16:46:58 +02:00
2019-11-30 23:03:30 +01:00
if ( profile & & profile - > timestampDescriptions . size ( ) + 1 < MAX_TIMESTAMP_QUERIES ) {
2022-09-20 17:48:19 +02:00
vkCmdWriteTimestamp ( cmd , VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT , profile - > queryPool , ( uint32_t ) profile - > timestampDescriptions . size ( ) ) ;
2019-09-18 00:16:08 +02:00
profile - > timestampDescriptions . push_back ( StepToString ( step ) ) ;
2019-08-21 16:46:58 +02:00
}
2020-05-21 08:38:48 -07:00
if ( emitLabels ) {
2022-09-20 17:48:19 +02:00
vkCmdEndDebugUtilsLabelEXT ( cmd ) ;
2020-05-21 08:38:48 -07:00
}
2019-08-08 14:07:53 +02:00
}
// Deleting all in one go should be easier on the instruction cache than deleting
// them as we go - and easier to debug because we can look backwards in the frame.
2022-10-12 16:21:54 +02:00
if ( ! keepSteps ) {
for ( auto step : steps ) {
delete step ;
}
steps . clear ( ) ;
2017-10-27 22:10:36 +02:00
}
2019-09-18 00:16:08 +02:00
if ( profile )
2020-09-24 23:52:03 +02:00
profile - > cpuEndTime = time_now_d ( ) ;
2017-10-27 22:10:36 +02:00
}
2018-04-13 17:32:36 +02:00
void VulkanQueueRunner : : ApplyMGSHack ( std : : vector < VKRStep * > & steps ) {
2019-08-08 11:49:35 +02:00
// Really need a sane way to express transforms of steps.
2018-04-13 17:32:36 +02:00
// We want to turn a sequence of copy,render(1),copy,render(1),copy,render(1) to copy,copy,copy,render(n).
for ( int i = 0 ; i < ( int ) steps . size ( ) - 3 ; i + + ) {
int last = - 1 ;
if ( ! ( steps [ i ] - > stepType = = VKRStepType : : COPY & &
steps [ i + 1 ] - > stepType = = VKRStepType : : RENDER & &
steps [ i + 2 ] - > stepType = = VKRStepType : : COPY & &
steps [ i + 1 ] - > render . numDraws = = 1 & &
steps [ i ] - > copy . dst = = steps [ i + 2 ] - > copy . dst ) )
continue ;
// Looks promising! Let's start by finding the last one.
for ( int j = i ; j < ( int ) steps . size ( ) ; j + + ) {
switch ( steps [ j ] - > stepType ) {
case VKRStepType : : RENDER :
if ( steps [ j ] - > render . numDraws > 1 )
last = j - 1 ;
2018-04-14 10:27:18 +02:00
// should really also check descriptor sets...
if ( steps [ j ] - > commands . size ( ) ) {
VkRenderData & cmd = steps [ j ] - > commands . back ( ) ;
if ( cmd . cmd = = VKRRenderCommand : : DRAW_INDEXED & & cmd . draw . count ! = 6 )
last = j - 1 ;
}
2018-04-13 17:32:36 +02:00
break ;
case VKRStepType : : COPY :
if ( steps [ j ] - > copy . dst ! = steps [ i ] - > copy . dst )
last = j - 1 ;
break ;
2018-06-24 07:35:19 -07:00
default :
break ;
2018-04-13 17:32:36 +02:00
}
if ( last ! = - 1 )
break ;
}
if ( last ! = - 1 ) {
// We've got a sequence from i to last that needs reordering.
// First, let's sort it, keeping the same length.
std : : vector < VKRStep * > copies ;
std : : vector < VKRStep * > renders ;
2018-04-13 18:05:04 +02:00
copies . reserve ( ( last - i ) / 2 ) ;
renders . reserve ( ( last - i ) / 2 ) ;
2018-04-13 17:32:36 +02:00
for ( int n = i ; n < = last ; n + + ) {
if ( steps [ n ] - > stepType = = VKRStepType : : COPY )
copies . push_back ( steps [ n ] ) ;
else if ( steps [ n ] - > stepType = = VKRStepType : : RENDER )
renders . push_back ( steps [ n ] ) ;
}
// Write the copies back. TODO: Combine them too.
for ( int j = 0 ; j < ( int ) copies . size ( ) ; j + + ) {
steps [ i + j ] = copies [ j ] ;
}
// Write the renders back (so they will be deleted properly).
for ( int j = 0 ; j < ( int ) renders . size ( ) ; j + + ) {
steps [ i + j + copies . size ( ) ] = renders [ j ] ;
}
2020-08-15 12:12:57 +02:00
_assert_ ( steps [ i + copies . size ( ) ] - > stepType = = VKRStepType : : RENDER ) ;
2018-04-13 17:32:36 +02:00
// Combine the renders.
for ( int j = 1 ; j < ( int ) renders . size ( ) ; j + + ) {
2019-05-10 23:26:34 +02:00
for ( int k = 0 ; k < ( int ) renders [ j ] - > commands . size ( ) ; k + + ) {
2018-04-13 17:32:36 +02:00
steps [ i + copies . size ( ) ] - > commands . push_back ( renders [ j ] - > commands [ k ] ) ;
}
steps [ i + copies . size ( ) + j ] - > stepType = VKRStepType : : RENDER_SKIP ;
}
// We're done.
break ;
}
}
2019-08-08 11:49:35 +02:00
// There's also a post processing effect using depals that's just brutal in some parts
// of the game.
for ( int i = 0 ; i < ( int ) steps . size ( ) - 3 ; i + + ) {
int last = - 1 ;
if ( ! ( steps [ i ] - > stepType = = VKRStepType : : RENDER & &
steps [ i + 1 ] - > stepType = = VKRStepType : : RENDER & &
steps [ i + 2 ] - > stepType = = VKRStepType : : RENDER & &
steps [ i ] - > render . numDraws = = 1 & &
steps [ i + 1 ] - > render . numDraws = = 1 & &
steps [ i + 2 ] - > render . numDraws = = 1 & &
2022-06-11 13:22:40 +02:00
steps [ i ] - > render . colorLoad = = VKRRenderPassLoadAction : : DONT_CARE & &
steps [ i + 1 ] - > render . colorLoad = = VKRRenderPassLoadAction : : KEEP & &
steps [ i + 2 ] - > render . colorLoad = = VKRRenderPassLoadAction : : DONT_CARE ) )
2019-08-08 11:49:35 +02:00
continue ;
VKRFramebuffer * depalFramebuffer = steps [ i ] - > render . framebuffer ;
VKRFramebuffer * targetFramebuffer = steps [ i + 1 ] - > render . framebuffer ;
// OK, found the start of a post-process sequence. Let's scan until we find the end.
2020-08-27 20:01:59 -07:00
for ( int j = i ; j < ( int ) steps . size ( ) - 3 ; j + + ) {
2019-08-08 11:49:35 +02:00
if ( ( ( j - i ) & 1 ) = = 0 ) {
// This should be a depal draw.
if ( steps [ j ] - > render . numDraws ! = 1 )
break ;
2022-06-11 13:22:40 +02:00
if ( steps [ j ] - > render . colorLoad ! = VKRRenderPassLoadAction : : DONT_CARE )
2019-08-08 11:49:35 +02:00
break ;
if ( steps [ j ] - > render . framebuffer ! = depalFramebuffer )
break ;
last = j ;
} else {
// This should be a target draw.
if ( steps [ j ] - > render . numDraws ! = 1 )
break ;
2022-06-11 13:22:40 +02:00
if ( steps [ j ] - > render . colorLoad ! = VKRRenderPassLoadAction : : KEEP )
2019-08-08 11:49:35 +02:00
break ;
if ( steps [ j ] - > render . framebuffer ! = targetFramebuffer )
break ;
last = j ;
}
}
if ( last = = - 1 )
continue ;
// Combine the depal renders.
for ( int j = i + 2 ; j < = last + 1 ; j + = 2 ) {
for ( int k = 0 ; k < ( int ) steps [ j ] - > commands . size ( ) ; k + + ) {
switch ( steps [ j ] - > commands [ k ] . cmd ) {
case VKRRenderCommand : : DRAW :
case VKRRenderCommand : : DRAW_INDEXED :
steps [ i ] - > commands . push_back ( steps [ j ] - > commands [ k ] ) ;
break ;
2020-03-01 14:15:10 +01:00
default :
break ;
2019-08-08 11:49:35 +02:00
}
}
steps [ j ] - > stepType = VKRStepType : : RENDER_SKIP ;
}
// Combine the target renders.
for ( int j = i + 3 ; j < = last ; j + = 2 ) {
for ( int k = 0 ; k < ( int ) steps [ j ] - > commands . size ( ) ; k + + ) {
switch ( steps [ j ] - > commands [ k ] . cmd ) {
case VKRRenderCommand : : DRAW :
case VKRRenderCommand : : DRAW_INDEXED :
steps [ i + 1 ] - > commands . push_back ( steps [ j ] - > commands [ k ] ) ;
break ;
2020-03-01 14:15:10 +01:00
default :
break ;
2019-08-08 11:49:35 +02:00
}
}
steps [ j ] - > stepType = VKRStepType : : RENDER_SKIP ;
}
// We're done - we only expect one of these sequences per frame.
break ;
}
2018-04-13 17:32:36 +02:00
}
2018-04-13 18:05:04 +02:00
void VulkanQueueRunner : : ApplySonicHack ( std : : vector < VKRStep * > & steps ) {
// We want to turn a sequence of render(3),render(1),render(6),render(1),render(6),render(1),render(3) to
// render(1), render(1), render(1), render(6), render(6), render(6)
for ( int i = 0 ; i < ( int ) steps . size ( ) - 4 ; i + + ) {
int last = - 1 ;
if ( ! ( steps [ i ] - > stepType = = VKRStepType : : RENDER & &
steps [ i + 1 ] - > stepType = = VKRStepType : : RENDER & &
steps [ i + 2 ] - > stepType = = VKRStepType : : RENDER & &
steps [ i + 3 ] - > stepType = = VKRStepType : : RENDER & &
steps [ i ] - > render . numDraws = = 3 & &
steps [ i + 1 ] - > render . numDraws = = 1 & &
steps [ i + 2 ] - > render . numDraws = = 6 & &
steps [ i + 3 ] - > render . numDraws = = 1 & &
steps [ i ] - > render . framebuffer = = steps [ i + 2 ] - > render . framebuffer & &
steps [ i + 1 ] - > render . framebuffer = = steps [ i + 3 ] - > render . framebuffer ) )
continue ;
// Looks promising! Let's start by finding the last one.
for ( int j = i ; j < ( int ) steps . size ( ) ; j + + ) {
switch ( steps [ j ] - > stepType ) {
case VKRStepType : : RENDER :
if ( ( j - i ) & 1 ) {
if ( steps [ j ] - > render . framebuffer ! = steps [ i + 1 ] - > render . framebuffer )
last = j - 1 ;
if ( steps [ j ] - > render . numDraws ! = 1 )
last = j - 1 ;
} else {
if ( steps [ j ] - > render . framebuffer ! = steps [ i ] - > render . framebuffer )
last = j - 1 ;
if ( steps [ j ] - > render . numDraws ! = 3 & & steps [ j ] - > render . numDraws ! = 6 )
last = j - 1 ;
}
2018-06-24 07:35:19 -07:00
break ;
default :
break ;
2018-04-13 18:05:04 +02:00
}
if ( last ! = - 1 )
break ;
}
if ( last ! = - 1 ) {
// We've got a sequence from i to last that needs reordering.
// First, let's sort it, keeping the same length.
std : : vector < VKRStep * > type1 ;
std : : vector < VKRStep * > type2 ;
type1 . reserve ( ( last - i ) / 2 ) ;
type2 . reserve ( ( last - i ) / 2 ) ;
for ( int n = i ; n < = last ; n + + ) {
if ( steps [ n ] - > render . framebuffer = = steps [ i ] - > render . framebuffer )
type1 . push_back ( steps [ n ] ) ;
else
type2 . push_back ( steps [ n ] ) ;
}
// Write the renders back in order. Same amount, so deletion will work fine.
for ( int j = 0 ; j < ( int ) type1 . size ( ) ; j + + ) {
steps [ i + j ] = type1 [ j ] ;
}
for ( int j = 0 ; j < ( int ) type2 . size ( ) ; j + + ) {
steps [ i + j + type1 . size ( ) ] = type2 [ j ] ;
}
// Combine the renders.
for ( int j = 1 ; j < ( int ) type1 . size ( ) ; j + + ) {
for ( int k = 0 ; k < ( int ) type1 [ j ] - > commands . size ( ) ; k + + ) {
steps [ i ] - > commands . push_back ( type1 [ j ] - > commands [ k ] ) ;
}
steps [ i + j ] - > stepType = VKRStepType : : RENDER_SKIP ;
}
for ( int j = 1 ; j < ( int ) type2 . size ( ) ; j + + ) {
for ( int k = 0 ; k < ( int ) type2 [ j ] - > commands . size ( ) ; k + + ) {
steps [ i + type1 . size ( ) ] - > commands . push_back ( type2 [ j ] - > commands [ k ] ) ;
}
steps [ i + j + type1 . size ( ) ] - > stepType = VKRStepType : : RENDER_SKIP ;
}
// We're done.
break ;
}
}
}
2020-08-27 10:02:50 +02:00
const char * AspectToString ( VkImageAspectFlags aspect ) {
switch ( aspect ) {
case VK_IMAGE_ASPECT_COLOR_BIT : return " COLOR " ;
case VK_IMAGE_ASPECT_DEPTH_BIT : return " DEPTH " ;
case VK_IMAGE_ASPECT_STENCIL_BIT : return " STENCIL " ;
case VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT : return " DEPTHSTENCIL " ;
default : return " UNUSUAL " ;
}
}
2022-11-05 22:06:53 +01:00
static const char * rpTypeDebugNames [ ] = {
" RENDER " ,
" RENDER_DEPTH " ,
" RENDER_INPUT " ,
" RENDER_DEPTH_INPUT " ,
" MV_RENDER " ,
" MV_RENDER_DEPTH " ,
" MV_RENDER_INPUT " ,
" MV_RENDER_DEPTH_INPUT " ,
2022-11-28 11:50:28 +01:00
" MS_RENDER " ,
" MS_RENDER_DEPTH " ,
" MS_RENDER_INPUT " ,
" MS_RENDER_DEPTH_INPUT " ,
" MS_MV_RENDER " ,
" MS_MV_RENDER_DEPTH " ,
" MS_MV_RENDER_INPUT " ,
" MS_MV_RENDER_DEPTH_INPUT " ,
2022-11-05 22:06:53 +01:00
" BACKBUF " ,
} ;
2019-08-21 16:46:58 +02:00
std : : string VulkanQueueRunner : : StepToString ( const VKRStep & step ) const {
char buffer [ 256 ] ;
switch ( step . stepType ) {
case VKRStepType : : RENDER :
2019-08-21 18:28:20 +02:00
{
int w = step . render . framebuffer ? step . render . framebuffer - > width : vulkan_ - > GetBackbufferWidth ( ) ;
int h = step . render . framebuffer ? step . render . framebuffer - > height : vulkan_ - > GetBackbufferHeight ( ) ;
2020-10-11 13:07:08 +02:00
int actual_w = step . render . renderArea . extent . width ;
int actual_h = step . render . renderArea . extent . height ;
2022-11-05 22:06:53 +01:00
const char * renderCmd = rpTypeDebugNames [ ( size_t ) step . render . renderPassType ] ;
2022-10-03 11:20:36 +02:00
snprintf ( buffer , sizeof ( buffer ) , " %s %s %s (draws: %d, %dx%d/%dx%d) " , renderCmd , step . tag , step . render . framebuffer ? step . render . framebuffer - > Tag ( ) : " " , step . render . numDraws , actual_w , actual_h , w , h ) ;
2019-08-21 16:46:58 +02:00
break ;
2019-08-21 18:28:20 +02:00
}
2019-08-21 16:46:58 +02:00
case VKRStepType : : COPY :
2022-09-07 12:37:45 +02:00
snprintf ( buffer , sizeof ( buffer ) , " COPY '%s' %s -> %s (%dx%d, %s) " , step . tag , step . copy . src - > Tag ( ) , step . copy . dst - > Tag ( ) , step . copy . srcRect . extent . width , step . copy . srcRect . extent . height , AspectToString ( step . copy . aspectMask ) ) ;
2019-08-21 16:46:58 +02:00
break ;
case VKRStepType : : BLIT :
2022-09-07 12:37:45 +02:00
snprintf ( buffer , sizeof ( buffer ) , " BLIT '%s' %s -> %s (%dx%d->%dx%d, %s) " , step . tag , step . copy . src - > Tag ( ) , step . copy . dst - > Tag ( ) , step . blit . srcRect . extent . width , step . blit . srcRect . extent . height , step . blit . dstRect . extent . width , step . blit . dstRect . extent . height , AspectToString ( step . blit . aspectMask ) ) ;
2019-08-21 16:46:58 +02:00
break ;
case VKRStepType : : READBACK :
2022-10-03 11:20:36 +02:00
snprintf ( buffer , sizeof ( buffer ) , " READBACK '%s' %s (%dx%d, %s) " , step . tag , step . readback . src ? step . readback . src - > Tag ( ) : " (backbuffer) " , step . readback . srcRect . extent . width , step . readback . srcRect . extent . height , AspectToString ( step . readback . aspectMask ) ) ;
2019-08-21 16:46:58 +02:00
break ;
case VKRStepType : : READBACK_IMAGE :
2020-08-27 10:02:50 +02:00
snprintf ( buffer , sizeof ( buffer ) , " READBACK_IMAGE '%s' (%dx%d) " , step . tag , step . readback_image . srcRect . extent . width , step . readback_image . srcRect . extent . height ) ;
2019-08-21 16:46:58 +02:00
break ;
case VKRStepType : : RENDER_SKIP :
2020-08-27 10:02:50 +02:00
snprintf ( buffer , sizeof ( buffer ) , " (RENDER_SKIP) %s " , step . tag ) ;
2019-08-21 16:46:58 +02:00
break ;
default :
buffer [ 0 ] = 0 ;
break ;
}
return std : : string ( buffer ) ;
}
2019-08-13 23:07:58 +02:00
// Ideally, this should be cheap enough to be applied to all games. At least on mobile, it's pretty
// much a guaranteed neutral or win in terms of GPU power. However, dependency calculation really
// must be perfect!
void VulkanQueueRunner : : ApplyRenderPassMerge ( std : : vector < VKRStep * > & steps ) {
// First let's count how many times each framebuffer is rendered to.
// If it's more than one, let's do our best to merge them. This can help God of War quite a bit.
2020-05-17 14:53:37 -07:00
std : : unordered_map < VKRFramebuffer * , int > counts ;
2019-08-13 23:07:58 +02:00
for ( int i = 0 ; i < ( int ) steps . size ( ) ; i + + ) {
if ( steps [ i ] - > stepType = = VKRStepType : : RENDER ) {
counts [ steps [ i ] - > render . framebuffer ] + + ;
}
}
2020-05-17 14:53:37 -07:00
auto mergeRenderSteps = [ ] ( VKRStep * dst , VKRStep * src ) {
// OK. Now, if it's a render, slurp up all the commands and kill the step.
// Also slurp up any pretransitions.
2022-09-01 10:52:35 +02:00
dst - > preTransitions . append ( src - > preTransitions ) ;
2020-05-17 14:53:37 -07:00
dst - > commands . insert ( dst - > commands . end ( ) , src - > commands . begin ( ) , src - > commands . end ( ) ) ;
2020-11-26 23:28:27 +01:00
MergeRenderAreaRectInto ( & dst - > render . renderArea , src - > render . renderArea ) ;
2020-05-17 15:02:51 -07:00
// So we don't consider it for other things, maybe doesn't matter.
src - > dependencies . clear ( ) ;
2020-05-17 14:53:37 -07:00
src - > stepType = VKRStepType : : RENDER_SKIP ;
2022-10-04 17:56:30 +02:00
dst - > render . numDraws + = src - > render . numDraws ;
dst - > render . numReads + = src - > render . numReads ;
2022-09-07 18:44:48 +02:00
dst - > render . pipelineFlags | = src - > render . pipelineFlags ;
dst - > render . renderPassType = MergeRPTypes ( dst - > render . renderPassType , src - > render . renderPassType ) ;
2020-05-17 14:53:37 -07:00
} ;
2020-05-17 15:02:51 -07:00
auto renderHasClear = [ ] ( const VKRStep * step ) {
2020-05-17 14:53:37 -07:00
const auto & r = step - > render ;
2022-06-11 13:22:40 +02:00
return r . colorLoad = = VKRRenderPassLoadAction : : CLEAR | | r . depthLoad = = VKRRenderPassLoadAction : : CLEAR | | r . stencilLoad = = VKRRenderPassLoadAction : : CLEAR ;
2020-05-17 14:53:37 -07:00
} ;
2019-08-13 23:07:58 +02:00
// Now, let's go through the steps. If we find one that is rendered to more than once,
// we'll scan forward and slurp up any rendering that can be merged across.
for ( int i = 0 ; i < ( int ) steps . size ( ) ; i + + ) {
if ( steps [ i ] - > stepType = = VKRStepType : : RENDER & & counts [ steps [ i ] - > render . framebuffer ] > 1 ) {
auto fb = steps [ i ] - > render . framebuffer ;
2019-08-21 21:30:36 +02:00
TinySet < VKRFramebuffer * , 8 > touchedFramebuffers ; // must be the same fast-size as the dependencies TinySet for annoying reasons.
2019-08-13 23:07:58 +02:00
for ( int j = i + 1 ; j < ( int ) steps . size ( ) ; j + + ) {
// If any other passes are reading from this framebuffer as-is, we cancel the scan.
2020-05-16 18:30:59 -07:00
if ( steps [ j ] - > dependencies . contains ( fb ) ) {
// Reading from itself means a KEEP, which is okay.
if ( steps [ j ] - > stepType ! = VKRStepType : : RENDER | | steps [ j ] - > render . framebuffer ! = fb )
break ;
}
2019-08-13 23:07:58 +02:00
switch ( steps [ j ] - > stepType ) {
case VKRStepType : : RENDER :
2020-05-17 15:02:51 -07:00
if ( steps [ j ] - > render . framebuffer = = fb ) {
// Prevent Unknown's example case from https://github.com/hrydgard/ppsspp/pull/12242
2020-05-19 00:44:42 +02:00
if ( renderHasClear ( steps [ j ] ) | | steps [ j ] - > dependencies . contains ( touchedFramebuffers ) ) {
2020-05-17 15:02:51 -07:00
goto done_fb ;
} else {
// Safe to merge, great.
mergeRenderSteps ( steps [ i ] , steps [ j ] ) ;
}
} else {
// Remember the framebuffer this wrote to. We can't merge with later passes that depend on these.
2019-08-21 21:30:36 +02:00
touchedFramebuffers . insert ( steps [ j ] - > render . framebuffer ) ;
}
2019-08-13 23:07:58 +02:00
break ;
2019-08-21 21:30:36 +02:00
case VKRStepType : : COPY :
2020-05-17 15:02:51 -07:00
if ( steps [ j ] - > copy . dst = = fb ) {
// Without framebuffer "renaming", we can't merge past a clobbered fb.
goto done_fb ;
}
2019-11-30 23:01:23 +01:00
touchedFramebuffers . insert ( steps [ j ] - > copy . dst ) ;
2019-08-21 21:30:36 +02:00
break ;
2019-08-13 23:07:58 +02:00
case VKRStepType : : BLIT :
2020-05-17 15:02:51 -07:00
if ( steps [ j ] - > blit . dst = = fb ) {
// Without framebuffer "renaming", we can't merge past a clobbered fb.
goto done_fb ;
}
2019-11-30 23:01:23 +01:00
touchedFramebuffers . insert ( steps [ j ] - > blit . dst ) ;
2019-08-13 23:07:58 +02:00
break ;
2019-08-21 21:13:56 +02:00
case VKRStepType : : READBACK :
2019-08-22 09:23:02 +02:00
// Not sure this has much effect, when executed READBACK is always the last step
// since we stall the GPU and wait immediately after.
2019-08-21 21:13:56 +02:00
break ;
2020-05-17 15:02:51 -07:00
case VKRStepType : : RENDER_SKIP :
case VKRStepType : : READBACK_IMAGE :
2020-03-01 14:15:10 +01:00
break ;
2020-05-17 15:02:51 -07:00
default :
// We added a new step? Might be unsafe.
goto done_fb ;
2019-08-13 23:07:58 +02:00
}
}
done_fb :
;
}
}
}
2020-08-27 10:02:50 +02:00
void VulkanQueueRunner : : LogSteps ( const std : : vector < VKRStep * > & steps , bool verbose ) {
INFO_LOG ( G3D , " =================== FRAME ==================== " ) ;
2018-02-11 07:03:23 -08:00
for ( size_t i = 0 ; i < steps . size ( ) ; i + + ) {
2017-10-31 23:49:13 +01:00
const VKRStep & step = * steps [ i ] ;
switch ( step . stepType ) {
case VKRStepType : : RENDER :
2020-08-27 10:02:50 +02:00
LogRenderPass ( step , verbose ) ;
2017-10-31 23:49:13 +01:00
break ;
case VKRStepType : : COPY :
LogCopy ( step ) ;
break ;
case VKRStepType : : BLIT :
LogBlit ( step ) ;
break ;
case VKRStepType : : READBACK :
LogReadback ( step ) ;
break ;
2017-11-05 18:03:53 -08:00
case VKRStepType : : READBACK_IMAGE :
LogReadbackImage ( step ) ;
break ;
2018-03-16 13:35:49 +01:00
case VKRStepType : : RENDER_SKIP :
2020-08-15 12:12:57 +02:00
INFO_LOG ( G3D , " (skipped render pass) " ) ;
2018-03-16 13:35:49 +01:00
break ;
2017-10-31 23:49:13 +01:00
}
}
2020-08-27 10:02:50 +02:00
INFO_LOG ( G3D , " ------------------- SUBMIT ------------------ " ) ;
2017-10-31 23:49:13 +01:00
}
2022-06-11 13:22:40 +02:00
const char * RenderPassActionName ( VKRRenderPassLoadAction a ) {
2020-05-18 21:33:09 -07:00
switch ( a ) {
2022-06-11 13:22:40 +02:00
case VKRRenderPassLoadAction : : CLEAR :
2020-05-18 21:33:09 -07:00
return " CLEAR " ;
2022-06-11 13:22:40 +02:00
case VKRRenderPassLoadAction : : DONT_CARE :
2020-05-18 21:33:09 -07:00
return " DONT_CARE " ;
2022-06-11 13:22:40 +02:00
case VKRRenderPassLoadAction : : KEEP :
2020-05-18 21:33:09 -07:00
return " KEEP " ;
}
return " ? " ;
}
2020-08-27 10:02:50 +02:00
const char * ImageLayoutToString ( VkImageLayout layout ) {
switch ( layout ) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : return " COLOR_ATTACHMENT " ;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : return " DEPTH_STENCIL_ATTACHMENT " ;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : return " SHADER_READ_ONLY " ;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : return " TRANSFER_SRC " ;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL : return " TRANSFER_DST " ;
case VK_IMAGE_LAYOUT_GENERAL : return " GENERAL " ;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : return " PRESENT_SRC_KHR " ;
case VK_IMAGE_LAYOUT_UNDEFINED : return " UNDEFINED " ;
default : return " (unknown) " ;
}
}
void VulkanQueueRunner : : LogRenderPass ( const VKRStep & pass , bool verbose ) {
2020-05-18 21:33:09 -07:00
const auto & r = pass . render ;
2022-09-07 12:37:45 +02:00
const char * framebuf = r . framebuffer ? r . framebuffer - > Tag ( ) : " backbuffer " ;
2020-08-27 10:02:50 +02:00
int w = r . framebuffer ? r . framebuffer - > width : vulkan_ - > GetBackbufferWidth ( ) ;
int h = r . framebuffer ? r . framebuffer - > height : vulkan_ - > GetBackbufferHeight ( ) ;
2022-06-11 13:22:40 +02:00
INFO_LOG ( G3D , " RENDER %s Begin(%s, draws: %d, %dx%d, %s, %s, %s) " , pass . tag , framebuf , r . numDraws , w , h , RenderPassActionName ( r . colorLoad ) , RenderPassActionName ( r . depthLoad ) , RenderPassActionName ( r . stencilLoad ) ) ;
2020-08-27 10:02:50 +02:00
// TODO: Log these in detail.
2022-03-13 12:03:48 -07:00
for ( int i = 0 ; i < ( int ) pass . preTransitions . size ( ) ; i + + ) {
2022-09-07 12:37:45 +02:00
INFO_LOG ( G3D , " PRETRANSITION: %s %s -> %s " , pass . preTransitions [ i ] . fb - > Tag ( ) , AspectToString ( pass . preTransitions [ i ] . aspect ) , ImageLayoutToString ( pass . preTransitions [ i ] . targetLayout ) ) ;
2020-08-27 10:02:50 +02:00
}
2020-08-27 16:53:26 +02:00
if ( verbose ) {
for ( auto & cmd : pass . commands ) {
switch ( cmd . cmd ) {
case VKRRenderCommand : : REMOVED :
INFO_LOG ( G3D , " (Removed) " ) ;
break ;
2022-02-19 20:40:27 +01:00
case VKRRenderCommand : : SELF_DEPENDENCY_BARRIER :
INFO_LOG ( G3D , " SelfBarrier() " ) ;
break ;
2019-06-16 20:29:38 +02:00
case VKRRenderCommand : : BIND_GRAPHICS_PIPELINE :
INFO_LOG ( G3D , " BindGraphicsPipeline(%x) " , ( int ) ( intptr_t ) cmd . graphics_pipeline . pipeline ) ;
break ;
case VKRRenderCommand : : BIND_COMPUTE_PIPELINE :
INFO_LOG ( G3D , " BindComputePipeline(%x) " , ( int ) ( intptr_t ) cmd . compute_pipeline . pipeline ) ;
2020-08-27 16:53:26 +02:00
break ;
case VKRRenderCommand : : BLEND :
INFO_LOG ( G3D , " BlendColor(%08x) " , cmd . blendColor . color ) ;
break ;
case VKRRenderCommand : : CLEAR :
INFO_LOG ( G3D , " Clear " ) ;
break ;
case VKRRenderCommand : : DRAW :
INFO_LOG ( G3D , " Draw(%d) " , cmd . draw . count ) ;
break ;
case VKRRenderCommand : : DRAW_INDEXED :
INFO_LOG ( G3D , " DrawIndexed(%d) " , cmd . drawIndexed . count ) ;
break ;
case VKRRenderCommand : : SCISSOR :
INFO_LOG ( G3D , " 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 :
INFO_LOG ( G3D , " Stencil(ref=%d, compare=%d, write=%d) " , cmd . stencil . stencilRef , cmd . stencil . stencilCompareMask , cmd . stencil . stencilWriteMask ) ;
break ;
case VKRRenderCommand : : VIEWPORT :
INFO_LOG ( G3D , " 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 :
INFO_LOG ( G3D , " PushConstants(%d) " , cmd . push . size ) ;
break ;
2022-10-18 21:52:38 -07:00
case VKRRenderCommand : : DEBUG_ANNOTATION :
INFO_LOG ( G3D , " DebugAnnotation(%s) " , cmd . debugAnnotation . annotation ) ;
break ;
2018-06-24 07:35:19 -07:00
2020-08-27 16:53:26 +02:00
case VKRRenderCommand : : NUM_RENDER_COMMANDS :
break ;
}
2017-10-31 23:49:13 +01:00
}
}
2020-08-27 16:53:26 +02:00
INFO_LOG ( G3D , " Final: %s %s " , ImageLayoutToString ( pass . render . finalColorLayout ) , ImageLayoutToString ( pass . render . finalDepthStencilLayout ) ) ;
INFO_LOG ( G3D , " RENDER End(%s) - %d commands executed " , framebuf , ( int ) pass . commands . size ( ) ) ;
2017-10-31 23:49:13 +01:00
}
2019-08-21 21:13:56 +02:00
void VulkanQueueRunner : : LogCopy ( const VKRStep & step ) {
2020-08-15 12:12:57 +02:00
INFO_LOG ( G3D , " %s " , StepToString ( step ) . c_str ( ) ) ;
2017-10-31 23:49:13 +01:00
}
2019-08-21 21:13:56 +02:00
void VulkanQueueRunner : : LogBlit ( const VKRStep & step ) {
2020-08-15 12:12:57 +02:00
INFO_LOG ( G3D , " %s " , StepToString ( step ) . c_str ( ) ) ;
2017-10-31 23:49:13 +01:00
}
2019-08-21 21:13:56 +02:00
void VulkanQueueRunner : : LogReadback ( const VKRStep & step ) {
2020-08-15 12:12:57 +02:00
INFO_LOG ( G3D , " %s " , StepToString ( step ) . c_str ( ) ) ;
2017-10-31 23:49:13 +01:00
}
2019-08-21 21:13:56 +02:00
void VulkanQueueRunner : : LogReadbackImage ( const VKRStep & step ) {
2020-08-15 12:12:57 +02:00
INFO_LOG ( G3D , " %s " , StepToString ( step ) . c_str ( ) ) ;
2017-11-05 18:03:53 -08:00
}
2022-10-23 11:21:35 +02:00
void TransitionToOptimal ( VkCommandBuffer cmd , VkImage colorImage , VkImageLayout colorLayout , VkImage depthStencilImage , VkImageLayout depthStencilLayout , int numLayers , VulkanBarrier * recordBarrier ) {
2020-10-11 10:21:50 +02:00
if ( colorLayout ! = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ) {
2022-05-01 11:32:57 +02:00
VkPipelineStageFlags srcStageMask = 0 ;
VkAccessFlags srcAccessMask = 0 ;
2020-10-11 10:21:50 +02:00
switch ( colorLayout ) {
case VK_IMAGE_LAYOUT_UNDEFINED :
// No need to specify stage or access.
break ;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL :
// Already the right color layout. Unclear that we need to do a lot here..
break ;
case VK_IMAGE_LAYOUT_GENERAL :
// We came from the Mali workaround, and are transitioning back to COLOR_ATTACHMENT_OPTIMAL.
2022-05-01 11:32:57 +02:00
srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ;
srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
2020-10-11 10:21:50 +02:00
break ;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
2022-05-01 11:32:57 +02:00
srcAccessMask = VK_ACCESS_SHADER_READ_BIT ;
srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ;
2020-10-11 10:21:50 +02:00
break ;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL :
2022-05-01 11:32:57 +02:00
srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT ;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT ;
2020-10-11 10:21:50 +02:00
break ;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL :
2022-05-01 11:32:57 +02:00
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT ;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT ;
2020-10-11 10:21:50 +02:00
break ;
default :
2022-09-21 13:12:58 +02:00
_dbg_assert_msg_ ( false , " TransitionToOptimal: Unexpected color layout %d " , ( int ) colorLayout ) ;
2020-10-11 10:21:50 +02:00
break ;
}
2022-05-01 11:32:57 +02:00
recordBarrier - > TransitionImage (
2022-10-23 11:21:35 +02:00
colorImage , 0 , 1 , numLayers , VK_IMAGE_ASPECT_COLOR_BIT ,
2022-05-01 11:32:57 +02:00
colorLayout ,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ,
srcAccessMask ,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ,
srcStageMask ,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ) ;
2020-10-11 10:21:50 +02:00
}
if ( depthStencilImage ! = VK_NULL_HANDLE & & depthStencilLayout ! = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ) {
2022-05-01 11:32:57 +02:00
VkPipelineStageFlags srcStageMask = 0 ;
VkAccessFlags srcAccessMask = 0 ;
2020-10-11 10:21:50 +02:00
switch ( depthStencilLayout ) {
case VK_IMAGE_LAYOUT_UNDEFINED :
// No need to specify stage or access.
break ;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
// Already the right depth layout. Unclear that we need to do a lot here..
break ;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
2022-05-01 11:32:57 +02:00
srcAccessMask = VK_ACCESS_SHADER_READ_BIT ;
srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ;
2020-10-11 10:21:50 +02:00
break ;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL :
2022-05-01 11:32:57 +02:00
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT ;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT ;
2020-10-11 10:21:50 +02:00
break ;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL :
2022-05-01 11:32:57 +02:00
srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT ;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT ;
2020-10-11 10:21:50 +02:00
break ;
default :
2022-09-21 13:12:58 +02:00
_dbg_assert_msg_ ( false , " TransitionToOptimal: Unexpected depth layout %d " , ( int ) depthStencilLayout ) ;
2020-10-11 10:21:50 +02:00
break ;
}
2022-05-01 11:32:57 +02:00
recordBarrier - > TransitionImage (
2022-10-23 11:21:35 +02:00
depthStencilImage , 0 , 1 , numLayers , VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ,
2022-05-01 11:32:57 +02:00
depthStencilLayout ,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ,
srcAccessMask ,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT ,
srcStageMask ,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT ) ;
2020-10-11 10:21:50 +02:00
}
}
2022-10-23 11:21:35 +02:00
void TransitionFromOptimal ( VkCommandBuffer cmd , VkImage colorImage , VkImageLayout colorLayout , VkImage depthStencilImage , int numLayers , VkImageLayout depthStencilLayout ) {
2020-10-11 10:21:50 +02:00
VkPipelineStageFlags srcStageMask = 0 ;
VkPipelineStageFlags dstStageMask = 0 ;
// If layouts aren't optimal, transition them.
VkImageMemoryBarrier barrier [ 2 ] { } ;
int barrierCount = 0 ;
if ( colorLayout ! = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ) {
barrier [ 0 ] . sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER ;
barrier [ 0 ] . pNext = nullptr ;
srcStageMask | = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
barrier [ 0 ] . srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ;
// And the final transition.
// Don't need to transition it if VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.
switch ( colorLayout ) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
barrier [ 0 ] . dstAccessMask = VK_ACCESS_SHADER_READ_BIT ;
dstStageMask | = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ;
break ;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL :
barrier [ 0 ] . dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT ;
dstStageMask | = VK_PIPELINE_STAGE_TRANSFER_BIT ;
break ;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL :
barrier [ 0 ] . dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT ;
dstStageMask | = VK_PIPELINE_STAGE_TRANSFER_BIT ;
break ;
case VK_IMAGE_LAYOUT_UNDEFINED :
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL :
// Nothing to do.
break ;
default :
2022-09-21 13:12:58 +02:00
_dbg_assert_msg_ ( false , " TransitionFromOptimal: Unexpected final color layout %d " , ( int ) colorLayout ) ;
2020-10-11 10:21:50 +02:00
break ;
}
barrier [ 0 ] . oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ;
barrier [ 0 ] . newLayout = colorLayout ;
barrier [ 0 ] . image = colorImage ;
barrier [ 0 ] . subresourceRange . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
barrier [ 0 ] . subresourceRange . baseMipLevel = 0 ;
barrier [ 0 ] . subresourceRange . levelCount = 1 ;
2022-10-23 11:21:35 +02:00
barrier [ 0 ] . subresourceRange . layerCount = numLayers ;
2020-10-11 10:21:50 +02:00
barrier [ 0 ] . srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED ;
barrier [ 0 ] . dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED ;
barrierCount + + ;
}
if ( depthStencilImage & & depthStencilLayout ! = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ) {
2020-10-11 11:17:43 +02:00
barrier [ barrierCount ] . sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER ;
barrier [ barrierCount ] . pNext = nullptr ;
2020-10-11 10:21:50 +02:00
srcStageMask | = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT ;
2020-10-11 11:17:43 +02:00
barrier [ barrierCount ] . srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ;
2020-10-11 10:21:50 +02:00
switch ( depthStencilLayout ) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
2020-10-11 11:17:43 +02:00
barrier [ barrierCount ] . dstAccessMask | = VK_ACCESS_SHADER_READ_BIT ;
2020-10-11 10:21:50 +02:00
dstStageMask | = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ;
break ;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL :
2020-10-11 11:17:43 +02:00
barrier [ barrierCount ] . dstAccessMask | = VK_ACCESS_TRANSFER_READ_BIT ;
2020-10-11 10:21:50 +02:00
dstStageMask | = VK_PIPELINE_STAGE_TRANSFER_BIT ;
break ;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL :
2020-10-11 11:17:43 +02:00
barrier [ barrierCount ] . dstAccessMask | = VK_ACCESS_TRANSFER_READ_BIT ;
2020-10-11 10:21:50 +02:00
dstStageMask | = VK_PIPELINE_STAGE_TRANSFER_BIT ;
break ;
case VK_IMAGE_LAYOUT_UNDEFINED :
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
// Nothing to do.
break ;
default :
2022-09-21 13:12:58 +02:00
_dbg_assert_msg_ ( false , " TransitionFromOptimal: Unexpected final depth layout %d " , ( int ) depthStencilLayout ) ;
2020-10-11 10:21:50 +02:00
break ;
}
2020-10-11 11:17:43 +02:00
barrier [ barrierCount ] . oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ;
barrier [ barrierCount ] . newLayout = depthStencilLayout ;
barrier [ barrierCount ] . image = depthStencilImage ;
barrier [ barrierCount ] . subresourceRange . aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ;
barrier [ barrierCount ] . subresourceRange . baseMipLevel = 0 ;
barrier [ barrierCount ] . subresourceRange . levelCount = 1 ;
2022-10-23 11:21:35 +02:00
barrier [ barrierCount ] . subresourceRange . layerCount = numLayers ;
2020-10-11 11:17:43 +02:00
barrier [ barrierCount ] . srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED ;
barrier [ barrierCount ] . dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED ;
2020-10-11 10:21:50 +02:00
barrierCount + + ;
}
if ( barrierCount ) {
vkCmdPipelineBarrier ( cmd , srcStageMask , dstStageMask , VK_DEPENDENCY_BY_REGION_BIT , 0 , nullptr , 0 , nullptr , barrierCount , barrier ) ;
}
}
2017-10-27 22:10:36 +02:00
void VulkanQueueRunner : : PerformRenderPass ( const VKRStep & step , VkCommandBuffer cmd ) {
2022-09-01 10:52:35 +02:00
for ( size_t i = 0 ; i < step . preTransitions . size ( ) ; i + + ) {
const TransitionRequest & iter = step . preTransitions [ i ] ;
2017-11-22 12:24:05 +01:00
if ( iter . aspect = = VK_IMAGE_ASPECT_COLOR_BIT & & iter . fb - > color . layout ! = iter . targetLayout ) {
2022-05-01 11:32:57 +02:00
recordBarrier_ . TransitionImageAuto (
iter . fb - > color . image ,
0 ,
1 ,
2022-10-23 11:21:35 +02:00
iter . fb - > numLayers ,
2022-05-01 11:32:57 +02:00
VK_IMAGE_ASPECT_COLOR_BIT ,
iter . fb - > color . layout ,
iter . targetLayout
) ;
iter . fb - > color . layout = iter . targetLayout ;
2022-09-24 23:21:48 +02:00
} else if ( iter . fb - > depth . image ! = VK_NULL_HANDLE & & ( iter . aspect & ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) ) & & iter . fb - > depth . layout ! = iter . targetLayout ) {
2022-05-01 11:32:57 +02:00
recordBarrier_ . TransitionImageAuto (
iter . fb - > depth . image ,
0 ,
1 ,
2022-10-23 11:21:35 +02:00
iter . fb - > numLayers ,
2022-05-01 11:32:57 +02:00
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ,
iter . fb - > depth . layout ,
iter . targetLayout
) ;
iter . fb - > depth . layout = iter . targetLayout ;
2017-10-27 22:10:36 +02:00
}
}
2018-03-18 09:43:13 +01:00
// Don't execute empty renderpasses that keep the contents.
2022-06-11 13:22:40 +02:00
if ( step . commands . empty ( ) & & step . render . colorLoad = = VKRRenderPassLoadAction : : KEEP & & step . render . depthLoad = = VKRRenderPassLoadAction : : KEEP & & step . render . stencilLoad = = VKRRenderPassLoadAction : : KEEP ) {
2022-05-01 11:32:57 +02:00
// Flush the pending barrier
recordBarrier_ . Flush ( cmd ) ;
2017-11-08 17:01:38 +01:00
// Nothing to do.
2020-08-27 10:02:50 +02:00
// TODO: Though - a later step might have used this step's finalColorLayout etc to get things in a layout it expects.
// Should we just do a barrier? Or just let the later step deal with not having things in its preferred layout, like now?
2017-11-08 17:01:38 +01:00
return ;
}
2020-08-10 09:16:28 +02:00
// Write-after-write hazards. Fixed flicker in God of War on ARM (before we added another fix that removed these).
2022-05-01 10:05:26 +02:00
// These aren't so common so not bothering to combine the barrier with the pretransition one.
2020-08-10 09:16:28 +02:00
if ( step . render . framebuffer ) {
int n = 0 ;
int stage = 0 ;
2022-05-01 11:32:57 +02:00
2020-08-10 09:16:28 +02:00
if ( step . render . framebuffer - > color . layout = = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ) {
2022-05-01 11:32:57 +02:00
recordBarrier_ . TransitionImage (
step . render . framebuffer - > color . image ,
0 ,
1 ,
2022-10-23 11:21:35 +02:00
step . render . framebuffer - > numLayers ,
2022-05-01 11:32:57 +02:00
VK_IMAGE_ASPECT_COLOR_BIT ,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT ,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
) ;
2020-08-10 09:16:28 +02:00
}
2022-09-24 23:21:48 +02:00
if ( step . render . framebuffer - > depth . image ! = VK_NULL_HANDLE & & step . render . framebuffer - > depth . layout = = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ) {
2022-05-01 11:32:57 +02:00
recordBarrier_ . TransitionImage (
2022-05-01 15:16:50 -07:00
step . render . framebuffer - > depth . image ,
2022-05-01 11:32:57 +02:00
0 ,
1 ,
2022-10-23 11:21:35 +02:00
step . render . framebuffer - > numLayers ,
2022-05-01 11:32:57 +02:00
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT ,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT ,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
) ;
2020-08-10 09:16:28 +02:00
}
2019-08-08 15:15:52 +02:00
}
2020-08-27 11:48:43 +02:00
// This reads the layout of the color and depth images, and chooses a render pass using them that
// will transition to the desired final layout.
2022-09-17 00:36:43 +02:00
//
2022-05-01 11:32:57 +02:00
// NOTE: Flushes recordBarrier_.
2022-09-07 18:44:48 +02:00
VKRRenderPass * renderPass = PerformBindFramebufferAsRenderTarget ( step , cmd ) ;
2017-10-27 22:10:36 +02:00
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 ;
2022-09-03 22:04:01 +02:00
VKRGraphicsPipeline * lastGraphicsPipeline = nullptr ;
VKRComputePipeline * lastComputePipeline = nullptr ;
2019-06-16 20:29:38 +02:00
2017-10-27 22:10:36 +02:00
auto & commands = step . commands ;
2019-08-12 23:19:07 +02:00
// We can do a little bit of state tracking here to eliminate some calls into the driver.
// The stencil ones are very commonly mostly redundant so let's eliminate them where possible.
2020-12-13 00:20:13 +01:00
// Might also want to consider scissor and viewport.
VkPipeline lastPipeline = VK_NULL_HANDLE ;
2022-09-01 14:21:34 +02:00
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE ;
2022-11-07 22:27:02 +01:00
bool pipelineOK = false ;
2019-08-12 23:19:07 +02:00
int lastStencilWriteMask = - 1 ;
int lastStencilCompareMask = - 1 ;
int lastStencilReference = - 1 ;
2017-10-27 22:10:36 +02:00
2022-09-06 13:30:18 +02:00
const RenderPassType rpType = step . render . renderPassType ;
2022-10-26 00:24:43 +02:00
for ( size_t i = 0 ; i < commands . size ( ) ; i + + ) {
const VkRenderData & c = commands [ i ] ;
2017-10-27 22:10:36 +02:00
switch ( c . cmd ) {
2018-05-03 07:05:36 -07:00
case VKRRenderCommand : : REMOVED :
break ;
2019-06-16 20:29:38 +02:00
case VKRRenderCommand : : BIND_GRAPHICS_PIPELINE :
{
2022-09-03 22:04:01 +02:00
VKRGraphicsPipeline * graphicsPipeline = c . graphics_pipeline . pipeline ;
if ( graphicsPipeline ! = lastGraphicsPipeline ) {
2022-12-10 21:09:50 -08:00
VkSampleCountFlagBits fbSampleCount = fb ? fb - > sampleCount : VK_SAMPLE_COUNT_1_BIT ;
2022-11-28 18:20:30 +01:00
if ( RenderPassTypeHasMultisample ( rpType ) & & fbSampleCount ! = graphicsPipeline - > SampleCount ( ) ) {
2022-12-02 00:30:06 +01:00
// should have been invalidated.
_assert_msg_ ( graphicsPipeline - > SampleCount ( ) = = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM ,
" expected %d sample count, got %d " , fbSampleCount , graphicsPipeline - > SampleCount ( ) ) ;
2022-11-28 18:20:30 +01:00
}
2022-11-05 22:06:53 +01:00
if ( ! graphicsPipeline - > pipeline [ ( size_t ) rpType ] ) {
2022-09-07 18:44:48 +02:00
// NOTE: If render steps got merged, it can happen that, as they ended during recording,
// they didn't know their final render pass type so they created the wrong pipelines in EndCurRenderStep().
// Unfortunately I don't know if we can fix it in any more sensible place than here.
// Maybe a middle pass. But let's try to just block and compile here for now, this doesn't
// happen all that much.
2022-11-05 22:06:53 +01:00
graphicsPipeline - > pipeline [ ( size_t ) rpType ] = Promise < VkPipeline > : : CreateEmpty ( ) ;
2023-02-01 11:42:25 +01:00
graphicsPipeline - > Create ( vulkan_ , renderPass - > Get ( vulkan_ , rpType , fbSampleCount ) , rpType , fbSampleCount , time_now_d ( ) , - 1 ) ;
2022-09-07 18:44:48 +02:00
}
2022-11-05 22:06:53 +01:00
VkPipeline pipeline = graphicsPipeline - > pipeline [ ( size_t ) rpType ] - > BlockUntilReady ( ) ;
2022-11-28 18:20:30 +01:00
2022-09-03 22:04:01 +02:00
if ( pipeline ! = VK_NULL_HANDLE ) {
vkCmdBindPipeline ( cmd , VK_PIPELINE_BIND_POINT_GRAPHICS , pipeline ) ;
pipelineLayout = c . pipeline . pipelineLayout ;
lastGraphicsPipeline = graphicsPipeline ;
2022-11-07 22:27:02 +01:00
pipelineOK = true ;
} else {
pipelineOK = false ;
2022-09-03 22:04:01 +02:00
}
2022-11-07 22:27:02 +01:00
// Reset dynamic state so it gets refreshed with the new pipeline.
lastStencilWriteMask = - 1 ;
lastStencilCompareMask = - 1 ;
lastStencilReference = - 1 ;
2017-10-27 22:10:36 +02:00
}
break ;
2019-06-16 20:29:38 +02:00
}
case VKRRenderCommand : : BIND_COMPUTE_PIPELINE :
{
2022-09-03 22:04:01 +02:00
VKRComputePipeline * computePipeline = c . compute_pipeline . pipeline ;
if ( computePipeline ! = lastComputePipeline ) {
VkPipeline pipeline = computePipeline - > pipeline - > BlockUntilReady ( ) ;
if ( pipeline ! = VK_NULL_HANDLE ) {
vkCmdBindPipeline ( cmd , VK_PIPELINE_BIND_POINT_COMPUTE , pipeline ) ;
pipelineLayout = c . pipeline . pipelineLayout ;
lastComputePipeline = computePipeline ;
}
2019-06-16 20:29:38 +02:00
}
break ;
}
2017-10-27 22:10:36 +02:00
case VKRRenderCommand : : VIEWPORT :
2019-10-13 19:56:25 +02:00
if ( fb ! = nullptr ) {
vkCmdSetViewport ( cmd , 0 , 1 , & c . viewport . vp ) ;
} else {
const VkViewport & vp = c . viewport . vp ;
2019-10-13 21:23:49 +02:00
DisplayRect < float > rc { vp . x , vp . y , vp . width , vp . height } ;
2019-10-13 19:56:25 +02:00
RotateRectToDisplay ( rc , ( float ) vulkan_ - > GetBackbufferWidth ( ) , ( float ) vulkan_ - > GetBackbufferHeight ( ) ) ;
VkViewport final_vp ;
final_vp . x = rc . x ;
final_vp . y = rc . y ;
final_vp . width = rc . w ;
final_vp . height = rc . h ;
2019-10-13 20:43:26 +02:00
final_vp . maxDepth = vp . maxDepth ;
final_vp . minDepth = vp . minDepth ;
2019-10-13 19:56:25 +02:00
vkCmdSetViewport ( cmd , 0 , 1 , & final_vp ) ;
}
2017-10-27 22:10:36 +02:00
break ;
case VKRRenderCommand : : SCISSOR :
2019-10-13 19:56:25 +02:00
{
if ( fb ! = nullptr ) {
vkCmdSetScissor ( cmd , 0 , 1 , & c . scissor . scissor ) ;
} else {
// Rendering to backbuffer. Might need to rotate.
const VkRect2D & rc = c . scissor . scissor ;
2019-10-13 21:23:49 +02:00
DisplayRect < int > rotated_rc { rc . offset . x , rc . offset . y , ( int ) rc . extent . width , ( int ) rc . extent . height } ;
2019-10-13 19:56:25 +02:00
RotateRectToDisplay ( rotated_rc , vulkan_ - > GetBackbufferWidth ( ) , vulkan_ - > GetBackbufferHeight ( ) ) ;
2020-07-19 17:47:02 +02:00
_dbg_assert_ ( rotated_rc . x > = 0 ) ;
_dbg_assert_ ( rotated_rc . y > = 0 ) ;
2019-10-13 19:56:25 +02:00
VkRect2D finalRect = VkRect2D { { rotated_rc . x , rotated_rc . y } , { ( uint32_t ) rotated_rc . w , ( uint32_t ) rotated_rc . h } } ;
vkCmdSetScissor ( cmd , 0 , 1 , & finalRect ) ;
}
2019-10-13 20:37:31 +02:00
break ;
2019-10-13 19:56:25 +02:00
}
2017-10-27 22:10:36 +02:00
case VKRRenderCommand : : BLEND :
2019-10-13 21:15:01 +02:00
{
float bc [ 4 ] ;
Uint8x4ToFloat4 ( bc , c . blendColor . color ) ;
vkCmdSetBlendConstants ( cmd , bc ) ;
2017-10-27 22:10:36 +02:00
break ;
2019-10-13 21:15:01 +02:00
}
2017-10-27 22:10:36 +02:00
2022-02-19 20:40:27 +01:00
case VKRRenderCommand : : SELF_DEPENDENCY_BARRIER :
{
_assert_ ( step . render . pipelineFlags & PipelineFlags : : USES_INPUT_ATTACHMENT ) ;
2022-12-10 21:09:50 -08:00
_assert_ ( fb ) ;
2022-02-19 20:40:27 +01:00
VulkanBarrier barrier ;
2022-12-10 21:09:50 -08:00
if ( fb - > sampleCount ! = VK_SAMPLE_COUNT_1_BIT ) {
2022-11-28 11:50:28 +01:00
// Rendering is happening to the multisample buffer, not the color buffer.
2022-12-10 21:09:50 -08:00
SelfDependencyBarrier ( fb - > msaaColor , VK_IMAGE_ASPECT_COLOR_BIT , & barrier ) ;
2022-11-28 11:50:28 +01:00
} else {
2022-12-10 21:09:50 -08:00
SelfDependencyBarrier ( fb - > color , VK_IMAGE_ASPECT_COLOR_BIT , & barrier ) ;
2022-11-28 11:50:28 +01:00
}
2022-02-19 20:40:27 +01:00
barrier . Flush ( cmd ) ;
break ;
}
2017-11-01 14:18:39 +01:00
case VKRRenderCommand : : PUSH_CONSTANTS :
2022-11-07 22:27:02 +01:00
if ( pipelineOK ) {
vkCmdPushConstants ( cmd , pipelineLayout , c . push . stages , c . push . offset , c . push . size , c . push . data ) ;
}
2017-11-01 14:18:39 +01:00
break ;
2017-10-27 22:10:36 +02:00
case VKRRenderCommand : : STENCIL :
2019-08-13 00:22:47 +02:00
if ( lastStencilWriteMask ! = c . stencil . stencilWriteMask ) {
lastStencilWriteMask = ( int ) c . stencil . stencilWriteMask ;
2019-08-12 23:19:07 +02:00
vkCmdSetStencilWriteMask ( cmd , VK_STENCIL_FRONT_AND_BACK , c . stencil . stencilWriteMask ) ;
}
if ( lastStencilCompareMask ! = c . stencil . stencilCompareMask ) {
lastStencilCompareMask = c . stencil . stencilCompareMask ;
vkCmdSetStencilCompareMask ( cmd , VK_STENCIL_FRONT_AND_BACK , c . stencil . stencilCompareMask ) ;
}
if ( lastStencilReference ! = c . stencil . stencilRef ) {
lastStencilReference = c . stencil . stencilRef ;
vkCmdSetStencilReference ( cmd , VK_STENCIL_FRONT_AND_BACK , c . stencil . stencilRef ) ;
}
2017-10-27 22:10:36 +02:00
break ;
case VKRRenderCommand : : DRAW_INDEXED :
2022-11-07 22:27:02 +01:00
if ( pipelineOK ) {
2022-12-16 13:03:44 +01:00
vkCmdBindDescriptorSets ( cmd , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & c . drawIndexed . ds , c . drawIndexed . numUboOffsets , c . drawIndexed . uboOffsets ) ;
2022-11-07 22:27:02 +01:00
vkCmdBindIndexBuffer ( cmd , c . drawIndexed . ibuffer , c . drawIndexed . ioffset , ( VkIndexType ) c . drawIndexed . indexType ) ;
VkDeviceSize voffset = c . drawIndexed . voffset ;
vkCmdBindVertexBuffers ( cmd , 0 , 1 , & c . drawIndexed . vbuffer , & voffset ) ;
vkCmdDrawIndexed ( cmd , c . drawIndexed . count , c . drawIndexed . instances , 0 , 0 , 0 ) ;
}
2017-10-27 22:10:36 +02:00
break ;
case VKRRenderCommand : : DRAW :
2022-11-07 22:27:02 +01:00
if ( pipelineOK ) {
2022-12-16 13:03:44 +01:00
vkCmdBindDescriptorSets ( cmd , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & c . draw . ds , c . draw . numUboOffsets , c . draw . uboOffsets ) ;
2022-11-07 22:27:02 +01:00
if ( c . draw . vbuffer ) {
vkCmdBindVertexBuffers ( cmd , 0 , 1 , & c . draw . vbuffer , & c . draw . voffset ) ;
}
vkCmdDraw ( cmd , c . draw . count , 1 , c . draw . offset , 0 ) ;
2017-11-01 14:18:39 +01:00
}
2017-10-27 22:10:36 +02:00
break ;
case VKRRenderCommand : : CLEAR :
{
2019-08-12 23:19:07 +02:00
// If we get here, we failed to merge a clear into a render pass load op. This is bad for perf.
2017-10-27 22:10:36 +02:00
int numAttachments = 0 ;
VkClearRect rc { } ;
rc . baseArrayLayer = 0 ;
2022-10-26 20:26:30 +02:00
rc . layerCount = 1 ; // In multiview mode, 1 means to replicate to all the active layers.
2017-11-09 16:58:59 +01:00
rc . rect . extent . width = ( uint32_t ) curWidth ;
rc . rect . extent . height = ( uint32_t ) curHeight ;
2020-08-09 21:44:58 +02:00
VkClearAttachment attachments [ 2 ] { } ;
2017-10-27 22:10:36 +02:00
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 ) {
2017-11-09 16:58:59 +01:00
attachment . clearValue . depthStencil . stencil = ( uint32_t ) c . clear . clearStencil ;
2017-10-27 22:10:36 +02:00
attachment . aspectMask | = VK_IMAGE_ASPECT_STENCIL_BIT ;
}
}
if ( numAttachments ) {
vkCmdClearAttachments ( cmd , numAttachments , attachments , 1 , & rc ) ;
}
break ;
}
2022-10-13 22:34:21 +02:00
case VKRRenderCommand : : DEBUG_ANNOTATION :
if ( vulkan_ - > Extensions ( ) . EXT_debug_utils ) {
VkDebugUtilsLabelEXT labelInfo { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT } ;
labelInfo . pLabelName = c . debugAnnotation . annotation ;
vkCmdInsertDebugUtilsLabelEXT ( cmd , & labelInfo ) ;
}
break ;
2017-10-27 22:10:36 +02:00
default :
2020-08-15 12:12:57 +02:00
ERROR_LOG ( G3D , " Unimpl queue command " ) ;
2022-10-26 11:23:12 +02:00
break ;
2017-10-27 22:10:36 +02:00
}
}
vkCmdEndRenderPass ( cmd ) ;
2018-03-18 12:03:00 +01:00
if ( fb ) {
2020-10-11 10:21:50 +02:00
// If the desired final layout aren't the optimal layout for rendering, transition.
2022-10-23 11:21:35 +02:00
TransitionFromOptimal ( cmd , fb - > color . image , step . render . finalColorLayout , fb - > depth . image , fb - > numLayers , step . render . finalDepthStencilLayout ) ;
2020-10-11 10:21:50 +02:00
2018-03-15 16:10:12 +01:00
fb - > color . layout = step . render . finalColorLayout ;
2017-11-22 12:24:05 +01:00
fb - > depth . layout = step . render . finalDepthStencilLayout ;
2017-10-27 22:10:36 +02:00
}
}
2022-09-07 18:44:48 +02:00
VKRRenderPass * VulkanQueueRunner : : PerformBindFramebufferAsRenderTarget ( const VKRStep & step , VkCommandBuffer cmd ) {
2022-09-06 13:30:18 +02:00
VKRRenderPass * renderPass ;
2017-11-08 17:01:38 +01:00
int numClearVals = 0 ;
2022-11-28 11:50:28 +01:00
VkClearValue clearVal [ 4 ] { } ;
2017-10-27 22:10:36 +02:00
VkFramebuffer framebuf ;
int w ;
int h ;
2022-09-23 13:54:14 +02:00
bool hasDepth = RenderPassTypeHasDepth ( step . render . renderPassType ) ;
2022-11-27 11:39:44 +01:00
VkSampleCountFlagBits sampleCount ;
2017-10-27 22:10:36 +02:00
if ( step . render . framebuffer ) {
2020-07-19 17:47:02 +02:00
_dbg_assert_ ( step . render . finalColorLayout ! = VK_IMAGE_LAYOUT_UNDEFINED ) ;
2017-11-22 12:24:05 +01:00
_dbg_assert_ ( step . render . finalDepthStencilLayout ! = VK_IMAGE_LAYOUT_UNDEFINED ) ;
2019-08-08 14:07:53 +02:00
2022-09-07 12:37:45 +02:00
RPKey key {
step . render . colorLoad , step . render . depthLoad , step . render . stencilLoad ,
step . render . colorStore , step . render . depthStore , step . render . stencilStore ,
} ;
renderPass = GetRenderPass ( key ) ;
2017-10-27 22:10:36 +02:00
VKRFramebuffer * fb = step . render . framebuffer ;
2022-10-23 11:21:35 +02:00
framebuf = fb - > Get ( renderPass , step . render . renderPassType ) ;
2022-11-27 11:39:44 +01:00
sampleCount = fb - > sampleCount ;
2022-10-18 00:26:10 +02:00
_dbg_assert_ ( framebuf ! = VK_NULL_HANDLE ) ;
2017-10-27 22:10:36 +02:00
w = fb - > width ;
h = fb - > height ;
2017-11-08 17:01:38 +01:00
2018-03-18 11:19:10 +01:00
// Mali driver on S8 (Android O) and S9 mishandles renderpasses that do just a clear
// and then no draw calls. Memory transaction elimination gets mis-flagged or something.
// To avoid this, we transition to GENERAL and back in this case (ARM-approved workaround).
// See pull request #10723.
bool maliBugWorkaround = step . render . numDraws = = 0 & &
2022-06-11 13:22:40 +02:00
step . render . colorLoad = = VKRRenderPassLoadAction : : CLEAR & &
2019-02-05 10:05:22 +01:00
vulkan_ - > GetPhysicalDeviceProperties ( ) . properties . driverVersion = = 0xaa9c4b29 ;
2018-03-18 11:19:10 +01:00
if ( maliBugWorkaround ) {
2022-10-23 11:21:35 +02:00
recordBarrier_ . TransitionImage ( fb - > color . image , 0 , 1 , fb - > numLayers , VK_IMAGE_ASPECT_COLOR_BIT ,
2018-03-18 11:19:10 +01:00
fb - > color . layout , VK_IMAGE_LAYOUT_GENERAL ,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ,
2022-05-01 11:32:57 +02:00
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ) ;
2018-03-18 11:19:10 +01:00
fb - > color . layout = VK_IMAGE_LAYOUT_GENERAL ;
}
2022-10-23 11:21:35 +02:00
TransitionToOptimal ( cmd , fb - > color . image , fb - > color . layout , fb - > depth . image , fb - > depth . layout , fb - > numLayers , & recordBarrier_ ) ;
2018-03-18 12:03:00 +01:00
2020-10-11 10:21:50 +02:00
// The transition from the optimal format happens after EndRenderPass, now that we don't
// do it as part of the renderpass itself anymore.
2017-10-27 22:10:36 +02:00
2022-11-28 11:50:28 +01:00
if ( sampleCount ! = VK_SAMPLE_COUNT_1_BIT ) {
// We don't initialize values for these.
numClearVals = hasDepth ? 2 : 1 ; // Skip the resolve buffers, don't need to clear those.
}
2022-06-11 13:22:40 +02:00
if ( step . render . colorLoad = = VKRRenderPassLoadAction : : CLEAR ) {
2022-11-28 11:50:28 +01:00
Uint8x4ToFloat4 ( clearVal [ numClearVals ] . color . float32 , step . render . clearColor ) ;
2017-10-27 22:10:36 +02:00
}
2022-11-28 11:50:28 +01:00
numClearVals + + ;
if ( hasDepth ) {
if ( step . render . depthLoad = = VKRRenderPassLoadAction : : CLEAR | | step . render . stencilLoad = = VKRRenderPassLoadAction : : CLEAR ) {
clearVal [ numClearVals ] . depthStencil . depth = step . render . clearDepth ;
clearVal [ numClearVals ] . depthStencil . stencil = step . render . clearStencil ;
}
numClearVals + + ;
2017-10-27 22:10:36 +02:00
}
2022-11-28 11:50:28 +01:00
_dbg_assert_ ( numClearVals ! = 3 ) ;
2017-10-27 22:10:36 +02:00
} else {
2022-09-06 13:30:18 +02:00
RPKey key {
VKRRenderPassLoadAction : : CLEAR , VKRRenderPassLoadAction : : CLEAR , VKRRenderPassLoadAction : : CLEAR ,
VKRRenderPassStoreAction : : STORE , VKRRenderPassStoreAction : : DONT_CARE , VKRRenderPassStoreAction : : DONT_CARE ,
} ;
renderPass = GetRenderPass ( key ) ;
2022-11-06 14:36:51 +01:00
if ( IsVREnabled ( ) ) {
2022-10-12 16:21:54 +02:00
framebuf = ( VkFramebuffer ) BindVRFramebuffer ( ) ;
} else {
framebuf = backbuffer_ ;
}
2022-09-07 12:37:45 +02:00
// Raw, rotated backbuffer size.
w = vulkan_ - > GetBackbufferWidth ( ) ;
h = vulkan_ - > GetBackbufferHeight ( ) ;
2017-11-05 22:14:53 -08:00
Uint8x4ToFloat4 ( clearVal [ 0 ] . color . float32 , step . render . clearColor ) ;
2022-09-23 13:54:14 +02:00
numClearVals = hasDepth ? 2 : 1 ; // We might do depth-less backbuffer in the future, though doubtful of the value.
2017-10-27 22:10:36 +02:00
clearVal [ 1 ] . depthStencil . depth = 0.0f ;
clearVal [ 1 ] . depthStencil . stencil = 0 ;
2022-11-27 11:39:44 +01:00
sampleCount = VK_SAMPLE_COUNT_1_BIT ;
2017-10-27 22:10:36 +02:00
}
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO } ;
2022-11-27 11:39:44 +01:00
rp_begin . renderPass = renderPass - > Get ( vulkan_ , step . render . renderPassType , sampleCount ) ;
2017-10-27 22:10:36 +02:00
rp_begin . framebuffer = framebuf ;
2020-10-11 13:07:08 +02:00
VkRect2D rc = step . render . renderArea ;
if ( ! step . render . framebuffer ) {
// Rendering to backbuffer, must rotate, just like scissors.
DisplayRect < int > rotated_rc { rc . offset . x , rc . offset . y , ( int ) rc . extent . width , ( int ) rc . extent . height } ;
RotateRectToDisplay ( rotated_rc , vulkan_ - > GetBackbufferWidth ( ) , vulkan_ - > GetBackbufferHeight ( ) ) ;
rc . offset . x = rotated_rc . x ;
rc . offset . y = rotated_rc . y ;
rc . extent . width = rotated_rc . w ;
rc . extent . height = rotated_rc . h ;
}
2022-05-01 11:32:57 +02:00
recordBarrier_ . Flush ( cmd ) ;
2020-10-11 13:07:08 +02:00
rp_begin . renderArea = rc ;
2017-10-27 22:10:36 +02:00
rp_begin . clearValueCount = numClearVals ;
rp_begin . pClearValues = numClearVals ? clearVal : nullptr ;
vkCmdBeginRenderPass ( cmd , & rp_begin , VK_SUBPASS_CONTENTS_INLINE ) ;
2022-09-07 18:44:48 +02:00
return renderPass ;
2017-10-27 22:10:36 +02:00
}
void VulkanQueueRunner : : PerformCopy ( const VKRStep & step , VkCommandBuffer cmd ) {
2022-05-01 11:54:47 +02:00
// The barrier code doesn't handle this case. We'd need to transition to GENERAL to do an intra-image copy.
_dbg_assert_ ( step . copy . src ! = step . copy . dst ) ;
2017-10-27 22:10:36 +02:00
VKRFramebuffer * src = step . copy . src ;
VKRFramebuffer * dst = step . copy . dst ;
2022-10-23 11:21:35 +02:00
int layerCount = std : : min ( step . copy . src - > numLayers , step . copy . dst - > numLayers ) ;
_dbg_assert_ ( step . copy . src - > numLayers > = step . copy . dst - > numLayers ) ;
// TODO: If dst covers exactly the whole destination, we can set up a UNDEFINED->TRANSFER_DST_OPTIMAL transition,
// which can potentially be more efficient.
2017-10-27 22:10:36 +02:00
// First source barriers.
if ( step . copy . aspectMask & VK_IMAGE_ASPECT_COLOR_BIT ) {
2022-12-02 15:25:02 +01:00
SetupTransitionToTransferSrc ( src - > color , VK_IMAGE_ASPECT_COLOR_BIT , & recordBarrier_ ) ;
SetupTransitionToTransferDst ( dst - > color , VK_IMAGE_ASPECT_COLOR_BIT , & recordBarrier_ ) ;
2017-10-27 22:10:36 +02:00
}
2020-08-27 10:02:50 +02:00
// We can't copy only depth or only stencil unfortunately - or can we?.
2017-10-27 22:10:36 +02:00
if ( step . copy . aspectMask & ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) ) {
2022-10-10 19:14:34 -07:00
_dbg_assert_ ( src - > depth . image ! = VK_NULL_HANDLE ) ;
2022-12-02 15:25:02 +01:00
SetupTransitionToTransferSrc ( src - > depth , VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT , & recordBarrier_ ) ;
2017-10-27 22:10:36 +02:00
if ( dst - > depth . layout ! = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) {
2022-05-01 11:54:47 +02:00
SetupTransitionToTransferDst ( dst - > depth , VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT , & recordBarrier_ ) ;
2020-08-27 16:53:26 +02:00
_dbg_assert_ ( dst - > depth . layout = = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) ;
2022-09-23 13:47:11 +02:00
} else {
2022-12-02 15:25:02 +01:00
// Kingdom Hearts: Subsequent copies twice to the same depth buffer without any other use.
2022-09-23 13:47:11 +02:00
// Not super sure how that happens, but we need a barrier to pass sync validation.
SetupTransferDstWriteAfterWrite ( dst - > depth , VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT , & recordBarrier_ ) ;
2017-10-27 22:10:36 +02:00
}
}
2022-12-02 15:25:02 +01:00
recordBarrier_ . Flush ( cmd ) ;
bool multisampled = src - > sampleCount ! = VK_SAMPLE_COUNT_1_BIT & & dst - > sampleCount ! = VK_SAMPLE_COUNT_1_BIT ;
if ( multisampled ) {
// If both the targets are multisampled, copy the msaa targets too.
// For that, we need to transition them from their normally permanent VK_*_ATTACHMENT_OPTIMAL layouts, and then back.
if ( step . copy . aspectMask & VK_IMAGE_ASPECT_COLOR_BIT ) {
SetupTransitionToTransferSrc ( src - > msaaColor , VK_IMAGE_ASPECT_COLOR_BIT , & recordBarrier_ ) ;
recordBarrier_ . Flush ( cmd ) ;
SetupTransitionToTransferDst ( dst - > msaaColor , VK_IMAGE_ASPECT_COLOR_BIT , & recordBarrier_ ) ;
recordBarrier_ . Flush ( cmd ) ;
}
if ( step . copy . aspectMask & ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) ) {
// Kingdom Hearts: Subsequent copies to the same depth buffer without any other use.
// Not super sure how that happens, but we need a barrier to pass sync validation.
SetupTransitionToTransferSrc ( src - > msaaDepth , VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT , & recordBarrier_ ) ;
recordBarrier_ . Flush ( cmd ) ;
SetupTransitionToTransferDst ( dst - > msaaDepth , VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT , & recordBarrier_ ) ;
recordBarrier_ . Flush ( cmd ) ;
}
}
2017-10-27 22:10:36 +02:00
2022-05-01 11:54:47 +02:00
recordBarrier_ . Flush ( cmd ) ;
2017-10-27 22:10:36 +02:00
2022-10-23 11:21:35 +02:00
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 = layerCount ;
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 = layerCount ;
copy . extent . width = step . copy . srcRect . extent . width ;
copy . extent . height = step . copy . srcRect . extent . height ;
copy . extent . depth = 1 ;
2017-10-27 22:10:36 +02:00
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 , & copy ) ;
2022-12-02 15:25:02 +01:00
if ( multisampled ) {
vkCmdCopyImage ( cmd , src - > msaaColor . image , src - > msaaColor . layout , dst - > msaaColor . image , dst - > msaaColor . layout , 1 , & copy ) ;
}
2017-10-27 22:10:36 +02:00
}
if ( step . copy . aspectMask & ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) ) {
2022-09-25 09:53:50 +02:00
_dbg_assert_ ( src - > depth . image ! = VK_NULL_HANDLE ) ;
_dbg_assert_ ( dst - > depth . image ! = VK_NULL_HANDLE ) ;
2020-08-27 10:02:50 +02:00
copy . srcSubresource . aspectMask = step . copy . aspectMask & ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) ;
copy . dstSubresource . aspectMask = step . copy . aspectMask & ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) ;
2017-10-27 22:10:36 +02:00
vkCmdCopyImage ( cmd , src - > depth . image , src - > depth . layout , dst - > depth . image , dst - > depth . layout , 1 , & copy ) ;
2022-12-02 15:25:02 +01:00
if ( multisampled ) {
vkCmdCopyImage ( cmd , src - > msaaDepth . image , src - > msaaDepth . layout , dst - > msaaDepth . image , dst - > msaaDepth . layout , 1 , & copy ) ;
}
}
if ( multisampled ) {
// Transition the MSAA surfaces back to optimal.
if ( step . copy . aspectMask & VK_IMAGE_ASPECT_COLOR_BIT ) {
recordBarrier_ . TransitionImage (
src - > msaaColor . image ,
0 ,
1 ,
src - > msaaColor . numLayers ,
VK_IMAGE_ASPECT_COLOR_BIT ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ,
VK_ACCESS_TRANSFER_READ_BIT ,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
) ;
recordBarrier_ . TransitionImage (
dst - > msaaColor . image ,
0 ,
1 ,
dst - > msaaColor . numLayers ,
VK_IMAGE_ASPECT_COLOR_BIT ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ,
VK_ACCESS_TRANSFER_WRITE_BIT ,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
) ;
src - > msaaColor . layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ;
dst - > msaaColor . layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ;
}
if ( step . copy . aspectMask & ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) ) {
recordBarrier_ . TransitionImage (
src - > msaaDepth . image ,
0 ,
1 ,
src - > msaaDepth . numLayers ,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ,
VK_ACCESS_TRANSFER_READ_BIT ,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
) ;
recordBarrier_ . TransitionImage (
dst - > msaaDepth . image ,
0 ,
1 ,
dst - > msaaDepth . numLayers ,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ,
VK_ACCESS_TRANSFER_WRITE_BIT ,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
) ;
src - > msaaDepth . layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ;
dst - > msaaDepth . layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ;
}
recordBarrier_ . Flush ( cmd ) ;
2017-10-27 22:10:36 +02:00
}
}
void VulkanQueueRunner : : PerformBlit ( const VKRStep & step , VkCommandBuffer cmd ) {
2022-05-01 11:54:47 +02:00
// The barrier code doesn't handle this case. We'd need to transition to GENERAL to do an intra-image copy.
_dbg_assert_ ( step . blit . src ! = step . blit . dst ) ;
2022-10-23 11:21:35 +02:00
int layerCount = std : : min ( step . blit . src - > numLayers , step . blit . dst - > numLayers ) ;
_dbg_assert_ ( step . blit . src - > numLayers > = step . blit . dst - > numLayers ) ;
2017-10-27 22:10:36 +02:00
VKRFramebuffer * src = step . blit . src ;
VKRFramebuffer * dst = step . blit . dst ;
// First source barriers.
if ( step . blit . aspectMask & VK_IMAGE_ASPECT_COLOR_BIT ) {
2022-12-02 15:25:02 +01:00
SetupTransitionToTransferSrc ( src - > color , VK_IMAGE_ASPECT_COLOR_BIT , & recordBarrier_ ) ;
SetupTransitionToTransferDst ( dst - > color , VK_IMAGE_ASPECT_COLOR_BIT , & recordBarrier_ ) ;
2017-10-27 22:10:36 +02:00
}
// We can't copy only depth or only stencil unfortunately.
if ( step . blit . aspectMask & ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) ) {
2022-09-25 09:53:50 +02:00
_dbg_assert_ ( src - > depth . image ! = VK_NULL_HANDLE ) ;
2022-10-13 22:38:04 +02:00
_dbg_assert_ ( dst - > depth . image ! = VK_NULL_HANDLE ) ;
2022-09-25 09:53:50 +02:00
2022-12-02 15:25:02 +01:00
SetupTransitionToTransferSrc ( src - > depth , VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT , & recordBarrier_ ) ;
SetupTransitionToTransferDst ( dst - > depth , VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT , & recordBarrier_ ) ;
2017-10-27 22:10:36 +02:00
}
2022-05-01 11:54:47 +02:00
recordBarrier_ . Flush ( cmd ) ;
2017-10-27 22:10:36 +02:00
2022-10-23 11:21:35 +02:00
// 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 = layerCount ;
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 = layerCount ;
2017-10-27 22:10:36 +02:00
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 ) ;
}
}
2022-05-01 11:54:47 +02:00
void VulkanQueueRunner : : SetupTransitionToTransferSrc ( VKRImage & img , VkImageAspectFlags aspect , VulkanBarrier * recordBarrier ) {
2022-12-02 15:25:02 +01:00
if ( img . layout = = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ) {
return ;
}
2022-05-01 11:54:47 +02:00
VkImageAspectFlags imageAspect = aspect ;
VkAccessFlags srcAccessMask = 0 ;
VkPipelineStageFlags srcStageMask = 0 ;
2017-10-27 22:10:36 +02:00
switch ( img . layout ) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL :
2022-05-01 11:54:47 +02:00
srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT ;
srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
2017-10-27 22:10:36 +02:00
break ;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
2022-05-01 11:54:47 +02:00
srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ;
srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT ;
2017-10-27 22:10:36 +02:00
break ;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL :
2022-05-01 11:54:47 +02:00
srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT ;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT ;
2017-10-27 22:10:36 +02:00
break ;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
2022-05-01 11:54:47 +02:00
srcAccessMask = VK_ACCESS_SHADER_READ_BIT ;
srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ;
2017-10-27 22:10:36 +02:00
break ;
default :
2020-07-19 17:47:02 +02:00
_dbg_assert_msg_ ( false , " Transition from this layout to transfer src not supported (%d) " , ( int ) img . layout ) ;
2017-11-29 19:07:07 +01:00
break ;
2017-10-27 22:10:36 +02:00
}
2022-05-01 11:54:47 +02:00
2018-06-09 18:27:29 -07:00
if ( img . format = = VK_FORMAT_D16_UNORM_S8_UINT | | img . format = = VK_FORMAT_D24_UNORM_S8_UINT | | img . format = = VK_FORMAT_D32_SFLOAT_S8_UINT ) {
// Barrier must specify both for combined depth/stencil buffers.
2022-08-13 12:37:06 -07:00
imageAspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ;
2018-06-09 18:27:29 -07:00
} else {
2022-08-13 12:37:06 -07:00
imageAspect = aspect ;
2018-06-09 18:27:29 -07:00
}
2022-05-01 11:54:47 +02:00
recordBarrier - > TransitionImage (
img . image ,
0 ,
1 ,
2022-10-23 11:21:35 +02:00
img . numLayers ,
2022-08-13 12:37:06 -07:00
imageAspect ,
2022-05-01 11:54:47 +02:00
img . layout ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
srcAccessMask ,
VK_ACCESS_TRANSFER_READ_BIT ,
srcStageMask ,
VK_PIPELINE_STAGE_TRANSFER_BIT
) ;
img . layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ;
2017-10-27 22:10:36 +02:00
}
2022-05-01 11:54:47 +02:00
void VulkanQueueRunner : : SetupTransitionToTransferDst ( VKRImage & img , VkImageAspectFlags aspect , VulkanBarrier * recordBarrier ) {
2022-12-02 15:25:02 +01:00
if ( img . layout = = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) {
return ;
}
2022-05-01 11:54:47 +02:00
VkImageAspectFlags imageAspect = aspect ;
VkAccessFlags srcAccessMask = 0 ;
VkPipelineStageFlags srcStageMask = 0 ;
if ( img . format = = VK_FORMAT_D16_UNORM_S8_UINT | | img . format = = VK_FORMAT_D24_UNORM_S8_UINT | | img . format = = VK_FORMAT_D32_SFLOAT_S8_UINT ) {
// Barrier must specify both for combined depth/stencil buffers.
imageAspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ;
} else {
imageAspect = aspect ;
}
2017-10-27 22:10:36 +02:00
switch ( img . layout ) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL :
2022-05-01 11:54:47 +02:00
srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ;
srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT ;
2017-10-27 22:10:36 +02:00
break ;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL :
2022-05-01 11:54:47 +02:00
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT ;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT ;
2017-10-27 22:10:36 +02:00
break ;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
2022-05-01 11:54:47 +02:00
srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ;
srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT ;
2017-10-27 22:10:36 +02:00
break ;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
2022-05-01 11:54:47 +02:00
srcAccessMask = VK_ACCESS_SHADER_READ_BIT ;
srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ;
2017-10-27 22:10:36 +02:00
break ;
default :
2020-07-19 17:47:02 +02:00
_dbg_assert_msg_ ( false , " Transition from this layout to transfer dst not supported (%d) " , ( int ) img . layout ) ;
2017-11-29 19:07:07 +01:00
break ;
2017-10-27 22:10:36 +02:00
}
2022-05-01 11:54:47 +02:00
recordBarrier - > TransitionImage (
img . image ,
0 ,
1 ,
2022-10-23 11:21:35 +02:00
img . numLayers ,
2022-05-01 11:54:47 +02:00
aspect ,
img . layout ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
srcAccessMask ,
VK_ACCESS_TRANSFER_WRITE_BIT ,
srcStageMask ,
VK_PIPELINE_STAGE_TRANSFER_BIT
) ;
img . layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ;
2017-10-27 22:10:36 +02:00
}
2017-10-28 18:03:27 +02:00
2022-09-23 13:47:11 +02:00
void VulkanQueueRunner : : SetupTransferDstWriteAfterWrite ( VKRImage & img , VkImageAspectFlags aspect , VulkanBarrier * recordBarrier ) {
VkImageAspectFlags imageAspect = aspect ;
VkAccessFlags srcAccessMask = 0 ;
VkPipelineStageFlags srcStageMask = 0 ;
if ( img . format = = VK_FORMAT_D16_UNORM_S8_UINT | | img . format = = VK_FORMAT_D24_UNORM_S8_UINT | | img . format = = VK_FORMAT_D32_SFLOAT_S8_UINT ) {
// Barrier must specify both for combined depth/stencil buffers.
imageAspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ;
} else {
imageAspect = aspect ;
}
_dbg_assert_ ( img . layout = = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ) ;
srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT ;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT ;
recordBarrier - > TransitionImage (
img . image ,
0 ,
1 ,
2022-10-23 11:21:35 +02:00
img . numLayers ,
2022-09-23 13:47:11 +02:00
aspect ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ,
VK_ACCESS_TRANSFER_WRITE_BIT ,
VK_ACCESS_TRANSFER_WRITE_BIT ,
VK_PIPELINE_STAGE_TRANSFER_BIT ,
VK_PIPELINE_STAGE_TRANSFER_BIT
) ;
}
2023-02-05 16:59:23 +01:00
void VulkanQueueRunner : : ResizeReadbackBuffer ( CachedReadback * readback , VkDeviceSize requiredSize ) {
if ( readback - > buffer & & requiredSize < = readback - > bufferSize ) {
2023-02-05 00:35:30 +01:00
return ;
}
2023-02-05 16:59:23 +01:00
if ( readback - > buffer ) {
vulkan_ - > Delete ( ) . QueueDeleteBufferAllocation ( readback - > buffer , readback - > allocation ) ;
2023-02-05 00:35:30 +01:00
}
2023-02-05 16:59:23 +01:00
readback - > bufferSize = requiredSize ;
2023-02-05 00:35:30 +01:00
VkDevice device = vulkan_ - > GetDevice ( ) ;
VkBufferCreateInfo buf { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO } ;
2023-02-05 16:59:23 +01:00
buf . size = readback - > bufferSize ;
2023-02-05 00:35:30 +01:00
buf . usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT ;
VmaAllocationCreateInfo allocCreateInfo { } ;
allocCreateInfo . usage = VMA_MEMORY_USAGE_GPU_TO_CPU ;
VmaAllocationInfo allocInfo { } ;
2023-02-05 16:59:23 +01:00
VkResult res = vmaCreateBuffer ( vulkan_ - > Allocator ( ) , & buf , & allocCreateInfo , & readback - > buffer , & readback - > allocation , & allocInfo ) ;
2023-02-05 00:35:30 +01:00
_assert_ ( res = = VK_SUCCESS ) ;
const VkMemoryType & memoryType = vulkan_ - > GetMemoryProperties ( ) . memoryTypes [ allocInfo . memoryType ] ;
2023-02-05 16:59:23 +01:00
readback - > isCoherent = ( memoryType . propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ) ! = 0 ;
2023-02-05 00:35:30 +01:00
}
2023-02-05 16:59:23 +01:00
void VulkanQueueRunner : : PerformReadback ( const VKRStep & step , VkCommandBuffer cmd , FrameData & frameData ) {
2017-11-15 13:18:29 +01:00
VkImage image ;
VkImageLayout copyLayout ;
// Special case for backbuffer readbacks.
if ( step . readback . src = = nullptr ) {
// We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT,
// and then back into it.
2022-10-23 11:21:35 +02:00
// Regarding layers, backbuffer currently only has one layer.
2022-10-18 00:26:10 +02:00
TransitionImageLayout2 ( cmd , backbufferImage_ , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ,
2017-11-15 13:18:29 +01:00
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , VK_PIPELINE_STAGE_TRANSFER_BIT ,
0 , VK_ACCESS_TRANSFER_READ_BIT ) ;
copyLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ;
image = backbufferImage_ ;
} else {
VKRImage * srcImage ;
if ( step . readback . aspectMask & VK_IMAGE_ASPECT_COLOR_BIT ) {
srcImage = & step . readback . src - > color ;
2018-06-09 18:27:29 -07:00
} else if ( step . readback . aspectMask & ( VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT ) ) {
2017-11-15 13:18:29 +01:00
srcImage = & step . readback . src - > depth ;
2022-10-10 19:14:34 -07:00
_dbg_assert_ ( srcImage - > image ! = VK_NULL_HANDLE ) ;
2018-06-09 18:27:29 -07:00
} else {
2020-07-19 17:47:02 +02:00
_dbg_assert_msg_ ( false , " No image aspect to readback? " ) ;
2017-11-29 19:07:07 +01:00
return ;
2017-11-15 13:18:29 +01:00
}
if ( srcImage - > layout ! = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ) {
2022-05-01 11:54:47 +02:00
SetupTransitionToTransferSrc ( * srcImage , step . readback . aspectMask , & recordBarrier_ ) ;
recordBarrier_ . Flush ( cmd ) ;
2017-11-15 13:18:29 +01:00
}
image = srcImage - > image ;
copyLayout = srcImage - > layout ;
}
2023-02-05 16:59:23 +01:00
// TODO: Handle different readback formats!
u32 readbackSizeInBytes = sizeof ( uint32_t ) * step . readback . srcRect . extent . width * step . readback . srcRect . extent . height ;
CachedReadback * cached = nullptr ;
if ( step . readback . delayed ) {
ReadbackKey key ;
key . framebuf = step . readback . src ;
key . width = step . readback . srcRect . extent . width ;
key . height = step . readback . srcRect . extent . height ;
// See if there's already a buffer we can reuse
cached = frameData . readbacks_ . Get ( key ) ;
if ( ! cached ) {
cached = new CachedReadback ( ) ;
cached - > bufferSize = 0 ;
frameData . readbacks_ . Insert ( key , cached ) ;
}
} else {
cached = & syncReadback_ ;
}
ResizeReadbackBuffer ( cached , readbackSizeInBytes ) ;
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 = step . readback . srcRect . extent . width ;
region . bufferImageHeight = step . readback . srcRect . extent . height ;
vkCmdCopyImageToBuffer ( cmd , image , copyLayout , cached - > buffer , 1 , & region ) ;
2017-10-28 18:03:27 +02:00
// NOTE: Can't read the buffer using the CPU here - need to sync first.
2017-11-15 13:18:29 +01:00
// If we copied from the backbuffer, transition it back.
if ( step . readback . src = = nullptr ) {
// We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT,
// and then back into it.
2022-10-23 11:21:35 +02:00
// Regarding layers, backbuffer currently only has one layer.
2022-10-18 00:26:10 +02:00
TransitionImageLayout2 ( cmd , backbufferImage_ , 0 , 1 , 1 , VK_IMAGE_ASPECT_COLOR_BIT ,
2017-11-15 13:18:29 +01:00
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ,
VK_PIPELINE_STAGE_TRANSFER_BIT , VK_PIPELINE_STAGE_ALL_COMMANDS_BIT ,
VK_ACCESS_TRANSFER_READ_BIT , 0 ) ;
copyLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ;
}
2017-10-28 18:03:27 +02:00
}
2017-11-05 18:03:53 -08:00
void VulkanQueueRunner : : PerformReadbackImage ( const VKRStep & step , VkCommandBuffer cmd ) {
// TODO: Clean this up - just reusing `SetupTransitionToTransferSrc`.
2020-05-31 19:35:19 +02:00
VKRImage srcImage { } ;
2017-11-05 18:03:53 -08:00
srcImage . image = step . readback_image . image ;
srcImage . layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ;
2022-12-04 00:48:32 +01:00
srcImage . numLayers = 1 ;
2017-11-05 18:03:53 -08:00
2022-05-01 11:54:47 +02:00
SetupTransitionToTransferSrc ( srcImage , VK_IMAGE_ASPECT_COLOR_BIT , & recordBarrier_ ) ;
recordBarrier_ . Flush ( cmd ) ;
2017-11-05 18:03:53 -08:00
2023-02-05 16:59:23 +01:00
ResizeReadbackBuffer ( & syncReadback_ , sizeof ( uint32_t ) * step . readback_image . srcRect . extent . width * step . readback_image . srcRect . extent . height ) ;
2017-11-12 09:53:46 +01:00
2017-11-05 18:03:53 -08:00
VkBufferImageCopy region { } ;
region . imageOffset = { step . readback_image . srcRect . offset . x , step . readback_image . srcRect . offset . y , 0 } ;
region . imageExtent = { step . readback_image . srcRect . extent . width , step . readback_image . srcRect . extent . height , 1 } ;
region . imageSubresource . aspectMask = VK_IMAGE_ASPECT_COLOR_BIT ;
region . imageSubresource . layerCount = 1 ;
region . imageSubresource . mipLevel = step . readback_image . mipLevel ;
region . bufferOffset = 0 ;
region . bufferRowLength = step . readback_image . srcRect . extent . width ;
region . bufferImageHeight = step . readback_image . srcRect . extent . height ;
2023-02-05 16:59:23 +01:00
vkCmdCopyImageToBuffer ( cmd , step . readback_image . image , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , syncReadback_ . buffer , 1 , & region ) ;
2017-11-05 18:03:53 -08:00
// Now transfer it back to a texture.
2022-10-23 11:21:35 +02:00
TransitionImageLayout2 ( cmd , step . readback_image . image , 0 , 1 , 1 , // I don't think we have any multilayer cases for regular textures. Above in PerformReadback, though..
2017-11-05 18:03:53 -08:00
VK_IMAGE_ASPECT_COLOR_BIT ,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ,
VK_PIPELINE_STAGE_TRANSFER_BIT , VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT ,
VK_ACCESS_TRANSFER_READ_BIT , VK_ACCESS_SHADER_READ_BIT ) ;
// NOTE: Can't read the buffer using the CPU here - need to sync first.
2019-03-25 21:38:01 +01:00
// Doing that will also act like a heavyweight barrier ensuring that device writes are visible on the host.
2017-11-05 18:03:53 -08:00
}
2023-02-05 16:59:23 +01:00
bool VulkanQueueRunner : : CopyReadbackBuffer ( FrameData & frameData , VKRFramebuffer * src , int width , int height , Draw : : DataFormat srcFormat , Draw : : DataFormat destFormat , int pixelStride , uint8_t * pixels ) {
CachedReadback * readback = & syncReadback_ ;
// Look up in readback cache.
if ( src ) {
ReadbackKey key ;
key . framebuf = src ;
key . width = width ;
key . height = height ;
CachedReadback * cached = frameData . readbacks_ . Get ( key ) ;
if ( cached ) {
readback = cached ;
} else {
// Didn't have a cached image ready yet
return false ;
}
}
if ( ! readback - > buffer )
return false ; // Didn't find anything in cache, or something has gone really wrong.
2018-11-24 17:14:15 +01:00
2017-10-28 18:03:27 +02:00
// Read back to the requested address in ram from buffer.
void * mappedData ;
2017-11-05 18:40:27 -08:00
const size_t srcPixelSize = DataFormatSizeInBytes ( srcFormat ) ;
2023-02-05 16:59:23 +01:00
VkResult res = vmaMapMemory ( vulkan_ - > Allocator ( ) , readback - > allocation , & mappedData ) ;
2019-03-25 21:38:01 +01:00
2017-11-20 11:57:54 +01:00
if ( res ! = VK_SUCCESS ) {
2020-08-15 12:12:57 +02:00
ERROR_LOG ( G3D , " CopyReadbackBuffer: vkMapMemory failed! result=%d " , ( int ) res ) ;
2023-02-05 16:59:23 +01:00
return false ;
2017-11-20 11:57:54 +01:00
}
2019-03-25 21:38:01 +01:00
2023-02-05 16:59:23 +01:00
if ( ! readback - > isCoherent ) {
vmaInvalidateAllocation ( vulkan_ - > Allocator ( ) , readback - > allocation , 0 , width * height * srcPixelSize ) ;
2023-02-05 10:52:52 +01:00
}
2019-03-25 21:38:01 +01:00
// TODO: Perform these conversions in a compute shader on the GPU.
2017-11-05 18:03:53 -08:00
if ( srcFormat = = Draw : : DataFormat : : R8G8B8A8_UNORM ) {
ConvertFromRGBA8888 ( pixels , ( const uint8_t * ) mappedData , pixelStride , width , width , height , destFormat ) ;
2017-11-15 13:18:29 +01:00
} else if ( srcFormat = = Draw : : DataFormat : : B8G8R8A8_UNORM ) {
ConvertFromBGRA8888 ( pixels , ( const uint8_t * ) mappedData , pixelStride , width , width , height , destFormat ) ;
2017-11-05 18:03:53 -08:00
} else if ( srcFormat = = destFormat ) {
2017-11-22 12:24:05 +01:00
// Can just memcpy when it matches no matter the format!
2017-11-05 18:03:53 -08:00
uint8_t * dst = pixels ;
const uint8_t * src = ( const uint8_t * ) mappedData ;
for ( int y = 0 ; y < height ; + + y ) {
memcpy ( dst , src , width * srcPixelSize ) ;
src + = width * srcPixelSize ;
dst + = pixelStride * srcPixelSize ;
}
2017-11-05 18:40:27 -08:00
} else if ( destFormat = = Draw : : DataFormat : : D32F ) {
ConvertToD32F ( pixels , ( const uint8_t * ) mappedData , pixelStride , width , width , height , srcFormat ) ;
2022-08-29 23:59:43 +02:00
} else if ( destFormat = = Draw : : DataFormat : : D16 ) {
ConvertToD16 ( pixels , ( const uint8_t * ) mappedData , pixelStride , width , width , height , srcFormat ) ;
2017-11-05 18:03:53 -08:00
} else {
2017-11-05 18:40:27 -08:00
// TODO: Maybe a depth conversion or something?
2020-08-15 12:12:57 +02:00
ERROR_LOG ( G3D , " CopyReadbackBuffer: Unknown format " ) ;
2017-11-22 12:24:05 +01:00
_assert_msg_ ( false , " CopyReadbackBuffer: Unknown src format %d " , ( int ) srcFormat ) ;
2017-11-05 18:03:53 -08:00
}
2023-02-04 23:52:38 +01:00
2023-02-05 16:59:23 +01:00
vmaUnmapMemory ( vulkan_ - > Allocator ( ) , readback - > allocation ) ;
return true ;
2017-10-28 18:03:27 +02:00
}