mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-07 23:31:13 +00:00
890 lines
25 KiB
C++
890 lines
25 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2015 Baldur Karlsson
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "vk_replay.h"
|
|
#include "vk_core.h"
|
|
#include "vk_resources.h"
|
|
|
|
#include "serialise/string_utils.h"
|
|
|
|
VulkanReplay::OutputWindow::OutputWindow() : wnd(NULL_WND_HANDLE), width(0), height(0),
|
|
dsimg(VK_NULL_HANDLE), dsmem(VK_NULL_HANDLE)
|
|
{
|
|
swap = VK_NULL_HANDLE;
|
|
for(size_t i=0; i < ARRAY_COUNT(colimg); i++)
|
|
{
|
|
colimg[i] = VK_NULL_HANDLE;
|
|
colview[i] = VK_NULL_HANDLE;
|
|
}
|
|
|
|
VkImageMemoryBarrier t = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL,
|
|
0, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_UNDEFINED,
|
|
0, 0, VK_NULL_HANDLE,
|
|
{ VK_IMAGE_ASPECT_COLOR, 0, 1, 0, 1 }
|
|
};
|
|
for(size_t i=0; i < ARRAY_COUNT(coltrans); i++)
|
|
coltrans[i] = t;
|
|
|
|
t.subresourceRange.aspect = VK_IMAGE_ASPECT_DEPTH;
|
|
depthtrans = t;
|
|
|
|
t.subresourceRange.aspect = VK_IMAGE_ASPECT_STENCIL;
|
|
stenciltrans = t;
|
|
}
|
|
|
|
void VulkanReplay::OutputWindow::SetCol(VkDeviceMemory mem, VkImage img)
|
|
{
|
|
}
|
|
|
|
void VulkanReplay::OutputWindow::SetDS(VkDeviceMemory mem, VkImage img)
|
|
{
|
|
}
|
|
|
|
void VulkanReplay::OutputWindow::MakeTargets(const VulkanFunctions &vk, VkDevice device, bool depth)
|
|
{
|
|
vk.vkDeviceWaitIdle(device);
|
|
|
|
for(size_t i=0; i < ARRAY_COUNT(colimg); i++)
|
|
{
|
|
if(colimg[i] != VK_NULL_HANDLE)
|
|
{
|
|
vk.vkDestroyAttachmentView(device, colview[i]);
|
|
colimg[i] = VK_NULL_HANDLE;
|
|
colview[i] = VK_NULL_HANDLE;
|
|
}
|
|
}
|
|
|
|
if(dsimg != VK_NULL_HANDLE)
|
|
{
|
|
vk.vkDestroyAttachmentView(device, dsview);
|
|
vk.vkDestroyImage(device, dsimg);
|
|
vk.vkFreeMemory(device, dsmem);
|
|
|
|
dsview = VK_NULL_HANDLE;
|
|
dsimg = VK_NULL_HANDLE;
|
|
dsmem = VK_NULL_HANDLE;
|
|
}
|
|
|
|
VkSwapChainWSI old = swap;
|
|
|
|
void *handleptr = NULL;
|
|
VkPlatformWSI platform = VK_PLATFORM_MAX_ENUM_WSI;
|
|
|
|
#if defined(WIN32)
|
|
static int dllLocator=0;
|
|
|
|
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const char *)&dllLocator, (HMODULE *)&handleptr);
|
|
platform = VK_PLATFORM_WIN32_WSI;
|
|
#elif defined(__linux__)
|
|
VkPlatformHandleXcbWSI handle;
|
|
handle.connection = connection;
|
|
handle.root = screen->root;
|
|
|
|
handleptr = &handle;
|
|
platform = VK_PLATFORM_X11_WSI;
|
|
#else
|
|
#error "unknown platform"
|
|
#endif
|
|
|
|
VkSurfaceDescriptionWindowWSI surfDesc = { VK_STRUCTURE_TYPE_SURFACE_DESCRIPTION_WINDOW_WSI, NULL, platform, handleptr, &wnd };
|
|
|
|
// VKTODOHIGH need to verify which present modes are present
|
|
VkSwapChainCreateInfoWSI swapInfo = {
|
|
VK_STRUCTURE_TYPE_SWAP_CHAIN_CREATE_INFO_WSI, NULL, (VkSurfaceDescriptionWSI *)&surfDesc,
|
|
2, VK_FORMAT_B8G8R8A8_UNORM, { width, height },
|
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT,
|
|
VK_SURFACE_TRANSFORM_NONE_WSI, 1, VK_PRESENT_MODE_MAILBOX_WSI,
|
|
old, true,
|
|
};
|
|
|
|
VkResult res = vk.vkCreateSwapChainWSI(device, &swapInfo, &swap);
|
|
RDCASSERT(res == VK_SUCCESS);
|
|
|
|
if(old != VK_NULL_HANDLE)
|
|
vk.vkDestroySwapChainWSI(device, old);
|
|
|
|
size_t sz;
|
|
res = vk.vkGetSwapChainInfoWSI(device, swap, VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI, &sz, NULL);
|
|
RDCASSERT(res == VK_SUCCESS);
|
|
|
|
numImgs = uint32_t(sz/sizeof(VkSwapChainImagePropertiesWSI));
|
|
|
|
VkSwapChainImagePropertiesWSI* imgs = new VkSwapChainImagePropertiesWSI[numImgs];
|
|
res = vk.vkGetSwapChainInfoWSI(device, swap, VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI, &sz, imgs);
|
|
RDCASSERT(res == VK_SUCCESS);
|
|
|
|
for(size_t i=0; i < numImgs; i++)
|
|
{
|
|
colimg[i] = imgs[i].image;
|
|
coltrans[i].image = imgs[i].image;
|
|
coltrans[i].oldLayout = coltrans[i].newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
}
|
|
|
|
if(depth)
|
|
{
|
|
VULKANNOTIMP("Allocating depth-stencil image");
|
|
|
|
/*
|
|
dsmem = mem;
|
|
dsimg = img;
|
|
depthtrans.image = stenciltrans.image = img;
|
|
depthtrans.oldLayout = depthtrans.newLayout =
|
|
stenciltrans.oldLayout = stenciltrans.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
*/
|
|
}
|
|
|
|
for(uint32_t i=0; i < numImgs; i++)
|
|
{
|
|
if(colimg[i] != VK_NULL_HANDLE)
|
|
{
|
|
VkAttachmentViewCreateInfo info = {
|
|
VK_STRUCTURE_TYPE_ATTACHMENT_VIEW_CREATE_INFO, NULL,
|
|
colimg[i], VK_FORMAT_B8G8R8A8_UNORM, 0, 0, 1,
|
|
0 };
|
|
|
|
vk.vkCreateAttachmentView(device, &info, &colview[i]);
|
|
}
|
|
}
|
|
|
|
if(dsimg != VK_NULL_HANDLE)
|
|
{
|
|
VkAttachmentViewCreateInfo info = {
|
|
VK_STRUCTURE_TYPE_ATTACHMENT_VIEW_CREATE_INFO, NULL,
|
|
dsimg, VK_FORMAT_D32_SFLOAT_S8_UINT, 0, 0, 1,
|
|
0 };
|
|
|
|
vk.vkCreateAttachmentView(device, &info, &dsview);
|
|
}
|
|
}
|
|
|
|
VulkanReplay::VulkanReplay()
|
|
{
|
|
m_pDriver = NULL;
|
|
m_Proxy = false;
|
|
|
|
m_OutputWinID = 1;
|
|
m_ActiveWinID = 0;
|
|
m_BindDepth = false;
|
|
}
|
|
|
|
void VulkanReplay::Shutdown()
|
|
{
|
|
delete m_pDriver;
|
|
}
|
|
|
|
APIProperties VulkanReplay::GetAPIProperties()
|
|
{
|
|
APIProperties ret;
|
|
|
|
ret.pipelineType = ePipelineState_D3D11;
|
|
ret.degraded = false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void VulkanReplay::ReadLogInitialisation()
|
|
{
|
|
m_pDriver->ReadLogInitialisation();
|
|
}
|
|
|
|
void VulkanReplay::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType)
|
|
{
|
|
m_pDriver->ReplayLog(frameID, startEventID, endEventID, replayType);
|
|
}
|
|
|
|
ResourceId VulkanReplay::GetLiveID(ResourceId id)
|
|
{
|
|
return m_pDriver->GetResourceManager()->GetLiveID(id);
|
|
}
|
|
|
|
void VulkanReplay::InitCallstackResolver()
|
|
{
|
|
m_pDriver->GetSerialiser()->InitCallstackResolver();
|
|
}
|
|
|
|
bool VulkanReplay::HasCallstacks()
|
|
{
|
|
return m_pDriver->GetSerialiser()->HasCallstacks();
|
|
}
|
|
|
|
Callstack::StackResolver *VulkanReplay::GetCallstackResolver()
|
|
{
|
|
return m_pDriver->GetSerialiser()->GetCallstackResolver();
|
|
}
|
|
|
|
vector<FetchFrameRecord> VulkanReplay::GetFrameRecord()
|
|
{
|
|
return m_pDriver->GetFrameRecord();
|
|
}
|
|
|
|
vector<DebugMessage> VulkanReplay::GetDebugMessages()
|
|
{
|
|
VULKANNOTIMP("GetDebugMessages");
|
|
return vector<DebugMessage>();
|
|
}
|
|
|
|
vector<ResourceId> VulkanReplay::GetTextures()
|
|
{
|
|
VULKANNOTIMP("GetTextures");
|
|
vector<ResourceId> texs;
|
|
|
|
ResourceId id;
|
|
VkImage fakeBBIm = VK_NULL_HANDLE;
|
|
VkDeviceMemory fakeBBMem = VK_NULL_HANDLE;
|
|
m_pDriver->GetFakeBB(id, fakeBBIm, fakeBBMem);
|
|
|
|
texs.push_back(id);
|
|
return texs;
|
|
}
|
|
|
|
vector<ResourceId> VulkanReplay::GetBuffers()
|
|
{
|
|
VULKANNOTIMP("GetBuffers");
|
|
return vector<ResourceId>();
|
|
}
|
|
|
|
void VulkanReplay::PickPixel(ResourceId texture, uint32_t x, uint32_t y, uint32_t sliceFace, uint32_t mip, uint32_t sample, float pixel[4])
|
|
{
|
|
//VULKANNOTIMP("PickPixel");
|
|
|
|
ResourceId resid;
|
|
VkImage fakeBBIm = VK_NULL_HANDLE;
|
|
VkDeviceMemory fakeBBMem = VK_NULL_HANDLE;
|
|
m_pDriver->GetFakeBB(resid, fakeBBIm, fakeBBMem);
|
|
|
|
VkDevice dev = m_pDriver->GetDev();
|
|
VkCmdBuffer cmd = m_pDriver->GetCmd();
|
|
VkQueue q = m_pDriver->GetQ();
|
|
const VulkanFunctions &vk = m_pDriver->m_Real;
|
|
|
|
VkDeviceMemory readbackmem = VK_NULL_HANDLE;
|
|
|
|
{
|
|
VkMemoryAllocInfo allocInfo = {
|
|
/*.sType =*/ VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO,
|
|
/*.pNext =*/ NULL,
|
|
/*.allocationSize =*/ 128,
|
|
/*.memoryTypeIndex =*/ 0, // VKTODOHIGH find appropriate memory type index
|
|
};
|
|
|
|
VkResult res = vk.vkAllocMemory(dev, &allocInfo, &readbackmem);
|
|
RDCASSERT(res == VK_SUCCESS);
|
|
|
|
VkBufferCreateInfo bufInfo = {
|
|
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL,
|
|
128, VK_BUFFER_USAGE_GENERAL, 0,
|
|
VK_SHARING_MODE_EXCLUSIVE, 0, NULL,
|
|
};
|
|
|
|
VkBuffer destbuf;
|
|
|
|
res = vk.vkCreateBuffer(dev, &bufInfo, &destbuf);
|
|
RDCASSERT(res == VK_SUCCESS);
|
|
|
|
res = vk.vkBindBufferMemory(dev, destbuf, readbackmem, 0);
|
|
RDCASSERT(res == VK_SUCCESS);
|
|
|
|
// VKTODOHIGH find out the actual current image state
|
|
VkImageMemoryBarrier fakeTrans = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL,
|
|
0, 0, VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL,
|
|
0, 0, fakeBBIm,
|
|
{ VK_IMAGE_ASPECT_COLOR, 0, 1, 0, 1 } };
|
|
|
|
VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT };
|
|
|
|
res = vk.vkBeginCommandBuffer(cmd, &beginInfo);
|
|
RDCASSERT(res == VK_SUCCESS);
|
|
|
|
void *barrier = (void *)&fakeTrans;
|
|
vk.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier);
|
|
fakeTrans.oldLayout = fakeTrans.newLayout;
|
|
|
|
VkBufferImageCopy region = {
|
|
0, 128, 1,
|
|
{ VK_IMAGE_ASPECT_COLOR, 0, 0}, { (int)x, (int)y, 0 },
|
|
{ 1, 1, 1 },
|
|
};
|
|
vk.vkCmdCopyImageToBuffer(cmd, fakeBBIm, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, destbuf, 1, ®ion);
|
|
|
|
fakeTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI;
|
|
vk.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier);
|
|
|
|
vk.vkEndCommandBuffer(cmd);
|
|
|
|
vk.vkQueueSubmit(q, 1, &cmd, VK_NULL_HANDLE);
|
|
|
|
vk.vkQueueWaitIdle(q);
|
|
}
|
|
|
|
// VKTODOHIGH ultra cheeky - map memory directly without copying
|
|
// to host-visible memory
|
|
byte *pData = NULL;
|
|
vk.vkMapMemory(dev, readbackmem, 0, 0, 0, (void **)&pData);
|
|
|
|
RDCASSERT(pData != NULL);
|
|
|
|
// VKTODOHIGH assuming BGRA
|
|
pixel[2] = float(pData[0])/255.0f;
|
|
pixel[1] = float(pData[1])/255.0f;
|
|
pixel[0] = float(pData[2])/255.0f;
|
|
pixel[3] = float(pData[3])/255.0f;
|
|
|
|
vk.vkUnmapMemory(dev, readbackmem);
|
|
|
|
vk.vkDeviceWaitIdle(dev);
|
|
|
|
vk.vkFreeMemory(dev, readbackmem);
|
|
}
|
|
|
|
uint32_t VulkanReplay::PickVertex(uint32_t frameID, uint32_t eventID, MeshDisplay cfg, uint32_t x, uint32_t y)
|
|
{
|
|
RDCUNIMPLEMENTED("PickVertex");
|
|
return ~0U;
|
|
}
|
|
|
|
bool VulkanReplay::RenderTexture(TextureDisplay cfg)
|
|
{
|
|
VULKANNOTIMP("RenderTexture");
|
|
return false;
|
|
}
|
|
|
|
void VulkanReplay::RenderCheckerboard(Vec3f light, Vec3f dark)
|
|
{
|
|
auto it = m_OutputWindows.find(m_ActiveWinID);
|
|
if(m_ActiveWinID == 0 || it == m_OutputWindows.end())
|
|
return;
|
|
|
|
OutputWindow &outw = it->second;
|
|
|
|
VULKANNOTIMP("RenderCheckerboard");
|
|
|
|
VkDevice dev = m_pDriver->GetDev();
|
|
VkCmdBuffer cmd = m_pDriver->GetCmd();
|
|
VkQueue q = m_pDriver->GetQ();
|
|
const VulkanFunctions &vk = m_pDriver->m_Real;
|
|
|
|
VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT };
|
|
|
|
VkResult res = vk.vkBeginCommandBuffer(cmd, &beginInfo);
|
|
|
|
outw.curcoltrans->newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
vk.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, (void **)&outw.curcoltrans);
|
|
outw.curcoltrans->oldLayout = outw.curcoltrans->newLayout;
|
|
|
|
VkClearColorValue clearColor = { { RANDF(0.0f, 1.0f), RANDF(0.0f, 1.0f), RANDF(0.0f, 1.0f), 1.0f, } };
|
|
vk.vkCmdClearColorImage(cmd, outw.colimg[outw.curidx], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, &clearColor, 1, &outw.curcoltrans->subresourceRange);
|
|
|
|
res = vk.vkEndCommandBuffer(cmd);
|
|
|
|
res = vk.vkQueueSubmit(q, 1, &cmd, VK_NULL_HANDLE);
|
|
}
|
|
|
|
void VulkanReplay::RenderHighlightBox(float w, float h, float scale)
|
|
{
|
|
RDCUNIMPLEMENTED("RenderHighlightBox");
|
|
}
|
|
|
|
ResourceId VulkanReplay::RenderOverlay(ResourceId texid, TextureDisplayOverlay overlay, uint32_t frameID, uint32_t eventID, const vector<uint32_t> &passEvents)
|
|
{
|
|
RDCUNIMPLEMENTED("RenderOverlay");
|
|
return ResourceId();
|
|
}
|
|
|
|
void VulkanReplay::RenderMesh(uint32_t frameID, uint32_t eventID, const vector<MeshFormat> &secondaryDraws, MeshDisplay cfg)
|
|
{
|
|
RDCUNIMPLEMENTED("RenderMesh");
|
|
}
|
|
|
|
bool VulkanReplay::CheckResizeOutputWindow(uint64_t id)
|
|
{
|
|
if(id == 0 || m_OutputWindows.find(id) == m_OutputWindows.end())
|
|
return false;
|
|
|
|
OutputWindow &outw = m_OutputWindows[id];
|
|
|
|
if(outw.wnd == NULL_WND_HANDLE)
|
|
return false;
|
|
|
|
int32_t w, h;
|
|
GetOutputWindowDimensions(id, w, h);
|
|
|
|
if(w != outw.width || h != outw.height)
|
|
{
|
|
outw.width = w;
|
|
outw.height = h;
|
|
|
|
// VKTODOHIGH Currently the resize code crashes - unsure why
|
|
if(outw.width > 0 && outw.height > 0 && 0)
|
|
{
|
|
bool depth = (outw.dsimg != VK_NULL_HANDLE);
|
|
|
|
outw.MakeTargets(m_pDriver->m_Real, m_pDriver->GetDev(), depth);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void VulkanReplay::BindOutputWindow(uint64_t id, bool depth)
|
|
{
|
|
m_ActiveWinID = id;
|
|
m_BindDepth = depth;
|
|
|
|
auto it = m_OutputWindows.find(id);
|
|
if(id == 0 || it == m_OutputWindows.end())
|
|
return;
|
|
|
|
OutputWindow &outw = it->second;
|
|
|
|
VkDevice dev = m_pDriver->GetDev();
|
|
VkCmdBuffer cmd = m_pDriver->GetCmd();
|
|
VkQueue q = m_pDriver->GetQ();
|
|
const VulkanFunctions &vk = m_pDriver->m_Real;
|
|
|
|
VkSemaphore sem;
|
|
VkSemaphoreCreateInfo semInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, NULL, VK_FENCE_CREATE_SIGNALED_BIT };
|
|
|
|
vk.vkCreateSemaphore(dev, &semInfo, &sem);
|
|
|
|
vk.vkAcquireNextImageWSI(dev, outw.swap, UINT64_MAX, sem, &outw.curidx);
|
|
|
|
outw.curcoltrans = &outw.coltrans[outw.curidx];
|
|
|
|
vk.vkQueueWaitSemaphore(q, sem);
|
|
|
|
vk.vkDestroySemaphore(dev, sem);
|
|
}
|
|
|
|
void VulkanReplay::ClearOutputWindowColour(uint64_t id, float col[4])
|
|
{
|
|
VULKANNOTIMP("ClearOutputWindowColour");
|
|
|
|
// VKTODOHIGH: same as FlipOutputWindow but do a colour clear
|
|
// ultimately these functions should push commands into a queue and there should be a
|
|
// more explicit start/end render functions (similar to BindOutputWindow, so it
|
|
// could start the command buffer, and an end function could end it and submit it)
|
|
}
|
|
|
|
void VulkanReplay::ClearOutputWindowDepth(uint64_t id, float depth, uint8_t stencil)
|
|
{
|
|
VULKANNOTIMP("ClearOutputWindowDepth");
|
|
|
|
// VKTODOHIGH: same as FlipOutputWindow but do a depth clear
|
|
}
|
|
|
|
void VulkanReplay::FlipOutputWindow(uint64_t id)
|
|
{
|
|
auto it = m_OutputWindows.find(id);
|
|
if(id == 0 || it == m_OutputWindows.end())
|
|
return;
|
|
|
|
VULKANNOTIMP("FlipOutputWindow");
|
|
|
|
OutputWindow &outw = it->second;
|
|
|
|
VkDevice dev = m_pDriver->GetDev();
|
|
VkCmdBuffer cmd = m_pDriver->GetCmd();
|
|
VkQueue q = m_pDriver->GetQ();
|
|
const VulkanFunctions &vk = m_pDriver->m_Real;
|
|
|
|
// copy fake backbuffer into actual backbuffer
|
|
|
|
ResourceId resid;
|
|
VkImage fakeBBIm = VK_NULL_HANDLE;
|
|
VkDeviceMemory fakeBBMem = VK_NULL_HANDLE;
|
|
m_pDriver->GetFakeBB(resid, fakeBBIm, fakeBBMem);
|
|
|
|
// VKTODOHIGH find out the actual current image state
|
|
VkImageMemoryBarrier fakeTrans = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL,
|
|
0, 0, VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL,
|
|
0, 0, fakeBBIm,
|
|
{ VK_IMAGE_ASPECT_COLOR, 0, 1, 0, 1 } };
|
|
|
|
VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT };
|
|
|
|
VkResult res = vk.vkBeginCommandBuffer(cmd, &beginInfo);
|
|
RDCASSERT(res == VK_SUCCESS);
|
|
|
|
void *barrier = (void *)&fakeTrans;
|
|
|
|
vk.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier);
|
|
fakeTrans.oldLayout = fakeTrans.newLayout;
|
|
|
|
outw.curcoltrans->newLayout = VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL;
|
|
vk.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, (void **)&outw.curcoltrans);
|
|
outw.curcoltrans->oldLayout = outw.curcoltrans->newLayout;
|
|
|
|
VkImageCopy region = {
|
|
{ VK_IMAGE_ASPECT_COLOR, 0, 0}, { 0, 0, 0 },
|
|
{ VK_IMAGE_ASPECT_COLOR, 0, 0}, { 0, 0, 0 },
|
|
{ RDCMIN(1280, outw.width), RDCMIN(720, outw.height), 1 },
|
|
};
|
|
vk.vkCmdCopyImage(cmd, fakeBBIm, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, outw.colimg[outw.curidx], VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL, 1, ®ion);
|
|
|
|
fakeTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI;
|
|
vk.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier);
|
|
|
|
outw.curcoltrans->newLayout = VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI;
|
|
vk.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1,(void **) &outw.curcoltrans);
|
|
outw.curcoltrans->oldLayout = outw.curcoltrans->newLayout;
|
|
|
|
vk.vkEndCommandBuffer(cmd);
|
|
|
|
vk.vkQueueSubmit(q, 1, &cmd, VK_NULL_HANDLE);
|
|
|
|
{
|
|
VkPresentInfoWSI presentInfo = { VK_STRUCTURE_TYPE_QUEUE_PRESENT_INFO_WSI, NULL, 1, &outw.swap, &outw.curidx };
|
|
|
|
vk.vkQueuePresentWSI(q, &presentInfo);
|
|
|
|
vk.vkQueueWaitIdle(q);
|
|
}
|
|
|
|
vk.vkDeviceWaitIdle(dev);
|
|
}
|
|
|
|
void VulkanReplay::DestroyOutputWindow(uint64_t id)
|
|
{
|
|
auto it = m_OutputWindows.find(id);
|
|
if(id == 0 || it == m_OutputWindows.end())
|
|
return;
|
|
|
|
OutputWindow &outw = it->second;
|
|
|
|
const VulkanFunctions &vk = m_pDriver->m_Real;
|
|
VkDevice device = m_pDriver->GetDev();
|
|
|
|
for(size_t i=0; i < ARRAY_COUNT(outw.colimg); i++)
|
|
{
|
|
if(outw.colimg[i] != VK_NULL_HANDLE)
|
|
{
|
|
vk.vkDestroyAttachmentView(device, outw.colview[i]);
|
|
}
|
|
}
|
|
|
|
if(outw.dsimg != VK_NULL_HANDLE)
|
|
{
|
|
vk.vkDestroyAttachmentView(device, outw.dsview);
|
|
vk.vkDestroyImage(device, outw.dsimg);
|
|
vk.vkFreeMemory(device, outw.dsmem);
|
|
}
|
|
|
|
vk.vkDestroySwapChainWSI(device, outw.swap);
|
|
|
|
m_OutputWindows.erase(it);
|
|
}
|
|
|
|
uint64_t VulkanReplay::MakeOutputWindow(void *wn, bool depth)
|
|
{
|
|
uint64_t id = m_OutputWinID;
|
|
m_OutputWinID++;
|
|
|
|
m_OutputWindows[id].SetWindowHandle(wn);
|
|
|
|
if(wn != NULL)
|
|
{
|
|
int32_t w, h;
|
|
GetOutputWindowDimensions(id, w, h);
|
|
|
|
m_OutputWindows[id].width = w;
|
|
m_OutputWindows[id].height = h;
|
|
|
|
m_OutputWindows[id].MakeTargets(m_pDriver->m_Real, m_pDriver->GetDev(), depth);
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
vector<byte> VulkanReplay::GetBufferData(ResourceId buff, uint32_t offset, uint32_t len)
|
|
{
|
|
RDCUNIMPLEMENTED("GetBufferData");
|
|
return vector<byte>();
|
|
}
|
|
|
|
bool VulkanReplay::IsRenderOutput(ResourceId id)
|
|
{
|
|
RDCUNIMPLEMENTED("IsRenderOutput");
|
|
return false;
|
|
}
|
|
|
|
void VulkanReplay::FileChanged()
|
|
{
|
|
}
|
|
|
|
FetchTexture VulkanReplay::GetTexture(ResourceId id)
|
|
{
|
|
VULKANNOTIMP("GetTexture");
|
|
|
|
FetchTexture ret;
|
|
ret.arraysize = 1;
|
|
ret.byteSize = 1280*720*4;
|
|
ret.creationFlags = eTextureCreate_SwapBuffer|eTextureCreate_SRV|eTextureCreate_RTV;
|
|
ret.cubemap = false;
|
|
ret.customName = false;
|
|
ret.depth = 1;
|
|
ret.width = 1280;
|
|
ret.height = 720;
|
|
ret.dimension = 2;
|
|
ret.ID = id;
|
|
ret.mips = 1;
|
|
ret.msQual = 0;
|
|
ret.msSamp = 1;
|
|
ret.name = "WSI Presentable Image";
|
|
ret.numSubresources = 1;
|
|
|
|
ret.format.compByteWidth = 1;
|
|
ret.format.compCount = 4;
|
|
ret.format.compType = eCompType_UNorm;
|
|
ret.format.rawType = 0;
|
|
ret.format.special = false;
|
|
ret.format.specialFormat = eSpecial_Unknown;
|
|
ret.format.srgbCorrected = false;
|
|
ret.format.strname = "B8G8R8A8_UNORM";
|
|
return ret;
|
|
}
|
|
|
|
FetchBuffer VulkanReplay::GetBuffer(ResourceId id)
|
|
{
|
|
RDCUNIMPLEMENTED("GetBuffer");
|
|
return FetchBuffer();
|
|
}
|
|
|
|
ShaderReflection *VulkanReplay::GetShader(ResourceId id)
|
|
{
|
|
RDCUNIMPLEMENTED("GetShader");
|
|
return NULL;
|
|
}
|
|
|
|
void VulkanReplay::SavePipelineState()
|
|
{
|
|
VULKANNOTIMP("SavePipelineState");
|
|
|
|
create_array_uninit(m_D3D11PipelineState.m_OM.RenderTargets, 1);
|
|
|
|
ResourceId id;
|
|
VkImage fakeBBIm = VK_NULL_HANDLE;
|
|
VkDeviceMemory fakeBBMem = VK_NULL_HANDLE;
|
|
m_pDriver->GetFakeBB(id, fakeBBIm, fakeBBMem);
|
|
|
|
m_D3D11PipelineState.m_OM.RenderTargets[0].Resource = id;
|
|
}
|
|
|
|
void VulkanReplay::FillCBufferVariables(ResourceId shader, uint32_t cbufSlot, vector<ShaderVariable> &outvars, const vector<byte> &data)
|
|
{
|
|
RDCUNIMPLEMENTED("FillCBufferVariables");
|
|
}
|
|
|
|
bool VulkanReplay::GetMinMax(ResourceId texid, uint32_t sliceFace, uint32_t mip, uint32_t sample, float *minval, float *maxval)
|
|
{
|
|
RDCUNIMPLEMENTED("GetMinMax");
|
|
return false;
|
|
}
|
|
|
|
bool VulkanReplay::GetHistogram(ResourceId texid, uint32_t sliceFace, uint32_t mip, uint32_t sample, float minval, float maxval, bool channels[4], vector<uint32_t> &histogram)
|
|
{
|
|
RDCUNIMPLEMENTED("GetHistogram");
|
|
return false;
|
|
}
|
|
|
|
void VulkanReplay::InitPostVSBuffers(uint32_t frameID, uint32_t eventID)
|
|
{
|
|
VULKANNOTIMP("VulkanReplay::InitPostVSBuffers");
|
|
}
|
|
|
|
vector<EventUsage> VulkanReplay::GetUsage(ResourceId id)
|
|
{
|
|
VULKANNOTIMP("GetUsage");
|
|
return vector<EventUsage>();
|
|
}
|
|
|
|
void VulkanReplay::SetContextFilter(ResourceId id, uint32_t firstDefEv, uint32_t lastDefEv)
|
|
{
|
|
RDCUNIMPLEMENTED("SetContextFilter");
|
|
}
|
|
|
|
void VulkanReplay::FreeTargetResource(ResourceId id)
|
|
{
|
|
RDCUNIMPLEMENTED("FreeTargetResource");
|
|
}
|
|
|
|
void VulkanReplay::FreeCustomShader(ResourceId id)
|
|
{
|
|
RDCUNIMPLEMENTED("FreeCustomShader");
|
|
}
|
|
|
|
MeshFormat VulkanReplay::GetPostVSBuffers(uint32_t frameID, uint32_t eventID, uint32_t instID, MeshDataStage stage)
|
|
{
|
|
MeshFormat ret;
|
|
RDCEraseEl(ret);
|
|
|
|
VULKANNOTIMP("VulkanReplay::GetPostVSBuffers");
|
|
|
|
return ret;
|
|
}
|
|
|
|
byte *VulkanReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, bool resolve, bool forceRGBA8unorm, float blackPoint, float whitePoint, size_t &dataSize)
|
|
{
|
|
RDCUNIMPLEMENTED("GetTextureData");
|
|
return NULL;
|
|
}
|
|
|
|
void VulkanReplay::ReplaceResource(ResourceId from, ResourceId to)
|
|
{
|
|
RDCUNIMPLEMENTED("ReplaceResource");
|
|
}
|
|
|
|
void VulkanReplay::RemoveReplacement(ResourceId id)
|
|
{
|
|
RDCUNIMPLEMENTED("RemoveReplacement");
|
|
}
|
|
|
|
vector<uint32_t> VulkanReplay::EnumerateCounters()
|
|
{
|
|
VULKANNOTIMP("EnumerateCounters");
|
|
return vector<uint32_t>();
|
|
}
|
|
|
|
void VulkanReplay::DescribeCounter(uint32_t counterID, CounterDescription &desc)
|
|
{
|
|
RDCUNIMPLEMENTED("DescribeCounter");
|
|
}
|
|
|
|
vector<CounterResult> VulkanReplay::FetchCounters(uint32_t frameID, uint32_t minEventID, uint32_t maxEventID, const vector<uint32_t> &counters)
|
|
{
|
|
RDCUNIMPLEMENTED("FetchCounters");
|
|
return vector<CounterResult>();
|
|
}
|
|
|
|
void VulkanReplay::BuildTargetShader(string source, string entry, const uint32_t compileFlags, ShaderStageType type, ResourceId *id, string *errors)
|
|
{
|
|
RDCUNIMPLEMENTED("BuildTargetShader");
|
|
}
|
|
|
|
void VulkanReplay::BuildCustomShader(string source, string entry, const uint32_t compileFlags, ShaderStageType type, ResourceId *id, string *errors)
|
|
{
|
|
RDCUNIMPLEMENTED("BuildCustomShader");
|
|
}
|
|
|
|
vector<PixelModification> VulkanReplay::PixelHistory(uint32_t frameID, vector<EventUsage> events, ResourceId target, uint32_t x, uint32_t y, uint32_t slice, uint32_t mip, uint32_t sampleIdx)
|
|
{
|
|
RDCUNIMPLEMENTED("VulkanReplay::PixelHistory");
|
|
return vector<PixelModification>();
|
|
}
|
|
|
|
ShaderDebugTrace VulkanReplay::DebugVertex(uint32_t frameID, uint32_t eventID, uint32_t vertid, uint32_t instid, uint32_t idx, uint32_t instOffset, uint32_t vertOffset)
|
|
{
|
|
RDCUNIMPLEMENTED("DebugVertex");
|
|
return ShaderDebugTrace();
|
|
}
|
|
|
|
ShaderDebugTrace VulkanReplay::DebugPixel(uint32_t frameID, uint32_t eventID, uint32_t x, uint32_t y, uint32_t sample, uint32_t primitive)
|
|
{
|
|
RDCUNIMPLEMENTED("DebugPixel");
|
|
return ShaderDebugTrace();
|
|
}
|
|
|
|
ShaderDebugTrace VulkanReplay::DebugThread(uint32_t frameID, uint32_t eventID, uint32_t groupid[3], uint32_t threadid[3])
|
|
{
|
|
RDCUNIMPLEMENTED("DebugThread");
|
|
return ShaderDebugTrace();
|
|
}
|
|
|
|
ResourceId VulkanReplay::ApplyCustomShader(ResourceId shader, ResourceId texid, uint32_t mip)
|
|
{
|
|
RDCUNIMPLEMENTED("ApplyCustomShader");
|
|
return ResourceId();
|
|
}
|
|
|
|
ResourceId VulkanReplay::CreateProxyTexture( FetchTexture templateTex )
|
|
{
|
|
RDCUNIMPLEMENTED("CreateProxyTexture");
|
|
return ResourceId();
|
|
}
|
|
|
|
void VulkanReplay::SetProxyTextureData(ResourceId texid, uint32_t arrayIdx, uint32_t mip, byte *data, size_t dataSize)
|
|
{
|
|
RDCUNIMPLEMENTED("SetProxyTextureData");
|
|
}
|
|
|
|
ResourceId VulkanReplay::CreateProxyBuffer(FetchBuffer templateBuf)
|
|
{
|
|
RDCUNIMPLEMENTED("CreateProxyBuffer");
|
|
return ResourceId();
|
|
}
|
|
|
|
void VulkanReplay::SetProxyBufferData(ResourceId bufid, byte *data, size_t dataSize)
|
|
{
|
|
RDCUNIMPLEMENTED("SetProxyTextureData");
|
|
}
|
|
|
|
const VulkanFunctions &GetRealVKFunctions();
|
|
|
|
ReplayCreateStatus Vulkan_CreateReplayDevice(const char *logfile, IReplayDriver **driver)
|
|
{
|
|
RDCDEBUG("Creating a VulkanReplay replay device");
|
|
|
|
#if defined(WIN32)
|
|
bool loaded = Process::LoadModule("vulkan.0.dll");
|
|
#elif defined(__linux__)
|
|
bool loaded = Process::LoadModule("libvulkan.so");
|
|
#else
|
|
#error "Unknown platform"
|
|
#endif
|
|
if(!loaded)
|
|
{
|
|
RDCERR("Failed to load vulkan library");
|
|
return eReplayCreate_APIInitFailed;
|
|
}
|
|
|
|
VkInitParams initParams;
|
|
RDCDriver driverType = RDC_Vulkan;
|
|
string driverName = "VulkanReplay";
|
|
if(logfile)
|
|
RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, (RDCInitParams *)&initParams);
|
|
|
|
if(initParams.SerialiseVersion != VkInitParams::VK_SERIALISE_VERSION)
|
|
{
|
|
RDCERR("Incompatible VulkanReplay serialise version, expected %d got %d", VkInitParams::VK_SERIALISE_VERSION, initParams.SerialiseVersion);
|
|
return eReplayCreate_APIIncompatibleVersion;
|
|
}
|
|
|
|
WrappedVulkan *vk = new WrappedVulkan(GetRealVKFunctions(), logfile);
|
|
vk->Initialise(initParams);
|
|
|
|
RDCLOG("Created device.");
|
|
VulkanReplay *replay = vk->GetReplay();
|
|
replay->SetProxy(logfile == NULL);
|
|
|
|
*driver = (IReplayDriver *)replay;
|
|
|
|
return eReplayCreate_Success;
|
|
}
|
|
|
|
static DriverRegistration VkDriverRegistration(RDC_Vulkan, "Vulkan", &Vulkan_CreateReplayDevice);
|