mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-27 20:31:02 +00:00
512 lines
20 KiB
C++
512 lines
20 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_manager.h"
|
|
#include "vk_core.h"
|
|
|
|
template<>
|
|
void Serialiser::Serialise(const char *name, ImageRegionState &el)
|
|
{
|
|
ScopedContext scope(this, name, "ImageRegionState", 0, true);
|
|
|
|
Serialise("range", el.subresourceRange);
|
|
Serialise("prevstate", el.oldLayout);
|
|
Serialise("state", el.newLayout);
|
|
}
|
|
|
|
bool VulkanResourceManager::SerialisableResource(ResourceId id, VkResourceRecord *record)
|
|
{
|
|
if(id == m_Core->GetContextResourceID())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// debugging logging for barriers
|
|
#if 0
|
|
#define TRDBG(...) RDCLOG(__VA_ARGS__)
|
|
#else
|
|
#define TRDBG(...)
|
|
#endif
|
|
|
|
template<typename SrcBarrierType>
|
|
void VulkanResourceManager::RecordSingleBarrier(vector< pair<ResourceId, ImageRegionState> > &dststates, ResourceId id, const SrcBarrierType &t, uint32_t nummips, uint32_t numslices)
|
|
{
|
|
bool done = false;
|
|
|
|
auto it = dststates.begin();
|
|
for(; it != dststates.end(); ++it)
|
|
{
|
|
// image barriers are handled by initially inserting one subresource range for each aspect,
|
|
// and whenever we need more fine-grained detail we split it immediately for one range for
|
|
// each subresource in that aspect. Thereafter if a barrier comes in that covers multiple
|
|
// subresources, we update all matching ranges.
|
|
|
|
// find the states matching this id
|
|
if(it->first < id) continue;
|
|
if(it->first != id) break;
|
|
|
|
if(it->second.subresourceRange.aspectMask & t.subresourceRange.aspectMask)
|
|
{
|
|
// we've found a range that completely matches our region, doesn't matter if that's
|
|
// a whole image and the barrier is the whole image, or it's one subresource.
|
|
// note that for images with only one array/mip slice (e.g. render targets) we'll never
|
|
// really have to worry about the else{} branch
|
|
if(it->second.subresourceRange.baseMipLevel == t.subresourceRange.baseMipLevel &&
|
|
it->second.subresourceRange.mipLevels == nummips &&
|
|
it->second.subresourceRange.baseArrayLayer == t.subresourceRange.baseArrayLayer &&
|
|
it->second.subresourceRange.arraySize == numslices)
|
|
{
|
|
// verify
|
|
//RDCASSERT(it->second.state == t.oldLayout);
|
|
|
|
// apply it (prevstate is from the start of all barriers accumulated, so only set once)
|
|
if(it->second.oldLayout == UNKNOWN_PREV_IMG_LAYOUT)
|
|
it->second.oldLayout = t.oldLayout;
|
|
it->second.newLayout = t.newLayout;
|
|
|
|
done = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// this handles the case where the barrier covers a number of subresources and we need
|
|
// to update each matching subresource. If the barrier was only one mip & array slice
|
|
// it would have hit the case above. Find each subresource within the range, update it,
|
|
// and continue (marking as done so whenever we stop finding matching ranges, we are
|
|
// satisfied.
|
|
//
|
|
// note that regardless of how we lay out our subresources (slice-major or mip-major) the new
|
|
// range could be sparse, but that's OK as we only break out of the loop once we go past the whole
|
|
// aspect. Any subresources that don't match the range, after the split, will fail to meet any
|
|
// of the handled cases, so we'll just continue processing.
|
|
if(it->second.subresourceRange.mipLevels == 1 &&
|
|
it->second.subresourceRange.arraySize == 1 &&
|
|
it->second.subresourceRange.baseMipLevel >= t.subresourceRange.baseMipLevel &&
|
|
it->second.subresourceRange.baseMipLevel < t.subresourceRange.baseMipLevel+nummips &&
|
|
it->second.subresourceRange.baseArrayLayer >= t.subresourceRange.baseArrayLayer &&
|
|
it->second.subresourceRange.baseArrayLayer < t.subresourceRange.baseArrayLayer+numslices)
|
|
{
|
|
// apply it (prevstate is from the start of all barriers accumulated, so only set once)
|
|
if(it->second.oldLayout == UNKNOWN_PREV_IMG_LAYOUT)
|
|
it->second.oldLayout = t.oldLayout;
|
|
it->second.newLayout = t.newLayout;
|
|
|
|
// continue as there might be more, but we're done
|
|
done = true;
|
|
continue;
|
|
}
|
|
// finally handle the case where we have a range that covers a whole image but we need to
|
|
// split it. If the barrier covered the whole image too it would have hit the very first
|
|
// case, so we know that the barrier doesn't cover the whole range.
|
|
// Also, if we've already done the split this case won't be hit and we'll either fall into
|
|
// the case above, or we'll finish as we've covered the whole barrier.
|
|
else if(it->second.subresourceRange.mipLevels > 1 || it->second.subresourceRange.arraySize > 1)
|
|
{
|
|
pair<ResourceId, ImageRegionState> existing = *it;
|
|
|
|
// remember where we were in the array, as after this iterators will be
|
|
// invalidated.
|
|
size_t offs = it - dststates.begin();
|
|
size_t count = it->second.subresourceRange.mipLevels * it->second.subresourceRange.arraySize;
|
|
|
|
// only insert count-1 as we want count entries total - one per subresource
|
|
dststates.insert(it, count-1, existing);
|
|
|
|
// it now points at the first subresource, but we need to modify the ranges
|
|
// to be valid
|
|
it = dststates.begin()+offs;
|
|
|
|
for(size_t i=0; i < count; i++)
|
|
{
|
|
it->second.subresourceRange.mipLevels = 1;
|
|
it->second.subresourceRange.arraySize = 1;
|
|
|
|
// slice-major
|
|
it->second.subresourceRange.baseArrayLayer = uint32_t(i / existing.second.subresourceRange.mipLevels);
|
|
it->second.subresourceRange.baseMipLevel = uint32_t(i % existing.second.subresourceRange.mipLevels);
|
|
it++;
|
|
}
|
|
|
|
// reset the iterator to point to the first subresource
|
|
it = dststates.begin()+offs;
|
|
|
|
// the loop will continue after this point and look at the next subresources
|
|
// so we need to check to see if the first subresource lies in the range here
|
|
if(it->second.subresourceRange.baseMipLevel >= t.subresourceRange.baseMipLevel &&
|
|
it->second.subresourceRange.baseMipLevel < t.subresourceRange.baseMipLevel+nummips &&
|
|
it->second.subresourceRange.baseArrayLayer >= t.subresourceRange.baseArrayLayer &&
|
|
it->second.subresourceRange.baseArrayLayer < t.subresourceRange.baseArrayLayer+numslices)
|
|
{
|
|
// apply it (prevstate is from the start of all barriers accumulated, so only set once)
|
|
if(it->second.oldLayout == UNKNOWN_PREV_IMG_LAYOUT)
|
|
it->second.oldLayout = t.oldLayout;
|
|
it->second.newLayout = t.newLayout;
|
|
|
|
// continue as there might be more, but we're done
|
|
done = true;
|
|
}
|
|
|
|
// continue processing from here
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we've gone past where the new subresource range would sit
|
|
if(it->second.subresourceRange.aspectMask > t.subresourceRange.aspectMask)
|
|
break;
|
|
|
|
// otherwise continue to try and find the subresource range
|
|
}
|
|
|
|
if(done) return;
|
|
|
|
// we don't have an existing barrier for this memory region, insert into place. it points to
|
|
// where it should be inserted
|
|
dststates.insert(it, std::make_pair(id, ImageRegionState(t.subresourceRange, t.oldLayout, t.newLayout)));
|
|
}
|
|
|
|
void VulkanResourceManager::RecordBarriers(vector< pair<ResourceId, ImageRegionState> > &states, map<ResourceId, ImageLayouts> &layouts,
|
|
uint32_t numBarriers, const VkImageMemoryBarrier *barriers)
|
|
{
|
|
TRDBG("Recording %u barriers", numBarriers);
|
|
|
|
for(uint32_t ti=0; ti < numBarriers; ti++)
|
|
{
|
|
const VkImageMemoryBarrier &t = barriers[ti];
|
|
|
|
ResourceId id = m_State < WRITING ? GetNonDispWrapper(t.image)->id : GetResID(t.image);
|
|
|
|
uint32_t nummips = t.subresourceRange.mipLevels;
|
|
uint32_t numslices = t.subresourceRange.arraySize;
|
|
if(nummips == VK_REMAINING_MIP_LEVELS) nummips = layouts[id].mipLevels - t.subresourceRange.baseMipLevel;
|
|
if(numslices == VK_REMAINING_ARRAY_LAYERS) numslices = layouts[id].arraySize - t.subresourceRange.baseArrayLayer;
|
|
|
|
RecordSingleBarrier(states, id, t, nummips, numslices);
|
|
}
|
|
|
|
TRDBG("Post-record, there are %u states", (uint32_t)states.size());
|
|
}
|
|
|
|
void VulkanResourceManager::MergeBarriers(vector< pair<ResourceId, ImageRegionState> > &dststates,
|
|
vector< pair<ResourceId, ImageRegionState> > &srcstates)
|
|
{
|
|
TRDBG("Merging %u states", (uint32_t)srcstates.size());
|
|
|
|
for(size_t ti=0; ti < srcstates.size(); ti++)
|
|
{
|
|
const ImageRegionState &t = srcstates[ti].second;
|
|
RecordSingleBarrier(dststates, srcstates[ti].first, t, t.subresourceRange.mipLevels, t.subresourceRange.arraySize);
|
|
}
|
|
|
|
TRDBG("Post-merge, there are %u states", (uint32_t)dststates.size());
|
|
}
|
|
|
|
void VulkanResourceManager::SerialiseImageStates(map<ResourceId, ImageLayouts> &states, vector<VkImageMemoryBarrier> &barriers)
|
|
{
|
|
Serialiser *localSerialiser = m_pSerialiser;
|
|
|
|
SERIALISE_ELEMENT(uint32_t, NumMems, (uint32_t)states.size());
|
|
|
|
auto srcit = states.begin();
|
|
|
|
vector< pair<ResourceId, ImageRegionState> > vec;
|
|
|
|
for(uint32_t i=0; i < NumMems; i++)
|
|
{
|
|
SERIALISE_ELEMENT(ResourceId, id, srcit->first);
|
|
SERIALISE_ELEMENT(uint32_t, NumStates, (uint32_t)srcit->second.subresourceStates.size());
|
|
|
|
ResourceId liveid;
|
|
if(m_State < WRITING && HasLiveResource(id))
|
|
liveid = GetLiveID(id);
|
|
auto dstit = states.find(id);
|
|
|
|
for(uint32_t m=0; m < NumStates; m++)
|
|
{
|
|
SERIALISE_ELEMENT(ImageRegionState, state, srcit->second.subresourceStates[m]);
|
|
|
|
if(m_State < WRITING && liveid != ResourceId() && srcit != states.end())
|
|
{
|
|
VkImageMemoryBarrier t;
|
|
t.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
t.pNext = NULL;
|
|
// these input masks aren't used, we need to apply a global memory barrier
|
|
// to memory each time we restart log replaying. These barriers are just
|
|
// to get images into the right layout
|
|
t.inputMask = 0;
|
|
t.outputMask = 0;
|
|
// MULTIDEVICE need to handle multiple queues
|
|
t.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
t.destQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
t.image = Unwrap(GetCurrentHandle<VkImage>(liveid));
|
|
t.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
t.newLayout = state.newLayout;
|
|
t.subresourceRange = state.subresourceRange;
|
|
barriers.push_back(t);
|
|
vec.push_back(std::make_pair(liveid, state));
|
|
}
|
|
}
|
|
|
|
if(m_State >= WRITING) srcit++;
|
|
}
|
|
|
|
ApplyBarriers(vec, states);
|
|
|
|
for(size_t i=0; i < vec.size(); i++)
|
|
barriers[i].oldLayout = vec[i].second.oldLayout;
|
|
|
|
// erase any do-nothing barriers
|
|
for(auto it=barriers.begin(); it != barriers.end();)
|
|
{
|
|
if(it->oldLayout == it->newLayout)
|
|
it = barriers.erase(it);
|
|
else
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void VulkanResourceManager::MarkSparseMapReferenced(SparseMapping *sparse)
|
|
{
|
|
if(sparse == NULL)
|
|
{
|
|
RDCERR("Unexpected NULL sparse mapping");
|
|
return;
|
|
}
|
|
|
|
for(size_t i=0; i < sparse->opaquemappings.size(); i++)
|
|
MarkResourceFrameReferenced(GetResID(sparse->opaquemappings[i].mem), eFrameRef_Read);
|
|
|
|
for(int a=0; a < VK_IMAGE_ASPECT_NUM; a++)
|
|
for(VkDeviceSize i=0; sparse->pages[a] && i < sparse->imgdim.width*sparse->imgdim.height*sparse->imgdim.depth; i++)
|
|
MarkResourceFrameReferenced(GetResID(sparse->pages[a][i].first), eFrameRef_Read);
|
|
}
|
|
|
|
void VulkanResourceManager::ApplyBarriers(vector< pair<ResourceId, ImageRegionState> > &states, map<ResourceId, ImageLayouts> &layouts)
|
|
{
|
|
TRDBG("Applying %u barriers", (uint32_t)states.size());
|
|
|
|
for(size_t ti=0; ti < states.size(); ti++)
|
|
{
|
|
ResourceId id = states[ti].first;
|
|
ImageRegionState &t = states[ti].second;
|
|
|
|
TRDBG("Applying barrier to %llu", GetOriginalID(id));
|
|
|
|
auto stit = layouts.find(id);
|
|
|
|
if(stit == layouts.end())
|
|
{
|
|
TRDBG("Didn't find ID in image layouts");
|
|
continue;
|
|
}
|
|
|
|
uint32_t nummips = t.subresourceRange.mipLevels;
|
|
uint32_t numslices = t.subresourceRange.arraySize;
|
|
if(nummips == VK_REMAINING_MIP_LEVELS) nummips = layouts[id].mipLevels;
|
|
if(numslices == VK_REMAINING_ARRAY_LAYERS) numslices = layouts[id].arraySize;
|
|
|
|
if(nummips == 0) nummips = 1;
|
|
if(numslices == 0) numslices = 1;
|
|
|
|
if(t.oldLayout == t.newLayout) continue;
|
|
|
|
TRDBG("Barrier of %s (%u->%u, %u->%u) from %s to %s",
|
|
ToStr::Get(t.subresourceRange.aspect).c_str(),
|
|
t.subresourceRange.baseMipLevel, t.subresourceRange.mipLevels,
|
|
t.subresourceRange.baseArrayLayer, t.subresourceRange.arraySize,
|
|
ToStr::Get(t.oldLayout).c_str(), ToStr::Get(t.newLayout).c_str());
|
|
|
|
bool done = false;
|
|
|
|
TRDBG("Matching image has %u subresource states", stit->second.subresourceStates.size());
|
|
|
|
auto it = stit->second.subresourceStates.begin();
|
|
for(; it != stit->second.subresourceStates.end(); ++it)
|
|
{
|
|
TRDBG(".. state %s (%u->%u, %u->%u) from %s to %s",
|
|
ToStr::Get(it->subresourceRange.aspect).c_str(),
|
|
it->range.baseMipLevel, it->range.mipLevels,
|
|
it->range.baseArrayLayer, it->range.arraySize,
|
|
ToStr::Get(it->oldLayout).c_str(), ToStr::Get(it->newLayout).c_str());
|
|
|
|
// image barriers are handled by initially inserting one subresource range for each aspect,
|
|
// and whenever we need more fine-grained detail we split it immediately for one range for
|
|
// each subresource in that aspect. Thereafter if a barrier comes in that covers multiple
|
|
// subresources, we update all matching ranges.
|
|
|
|
if(it->subresourceRange.aspectMask & t.subresourceRange.aspectMask)
|
|
{
|
|
// we've found a range that completely matches our region, doesn't matter if that's
|
|
// a whole image and the barrier is the whole image, or it's one subresource.
|
|
// note that for images with only one array/mip slice (e.g. render targets) we'll never
|
|
// really have to worry about the else{} branch
|
|
if(it->subresourceRange.baseMipLevel == t.subresourceRange.baseMipLevel &&
|
|
it->subresourceRange.mipLevels == nummips &&
|
|
it->subresourceRange.baseArrayLayer == t.subresourceRange.baseArrayLayer &&
|
|
it->subresourceRange.arraySize == numslices)
|
|
{
|
|
/*
|
|
RDCASSERT(t.prevstate == UNKNOWN_PREV_IMG_LAYOUT || it->state == UNKNOWN_PREV_IMG_LAYOUT || // renderdoc untracked/ignored
|
|
it->state == t.prevstate || // valid barrier
|
|
t.prevstate == VK_IMAGE_LAYOUT_UNDEFINED); // can barrier from UNDEFINED to any state
|
|
*/
|
|
t.oldLayout = it->newLayout;
|
|
it->newLayout = t.newLayout;
|
|
|
|
done = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// this handles the case where the barrier covers a number of subresources and we need
|
|
// to update each matching subresource. If the barrier was only one mip & array slice
|
|
// it would have hit the case above. Find each subresource within the range, update it,
|
|
// and continue (marking as done so whenever we stop finding matching ranges, we are
|
|
// satisfied.
|
|
//
|
|
// note that regardless of how we lay out our subresources (slice-major or mip-major) the new
|
|
// range could be sparse, but that's OK as we only break out of the loop once we go past the whole
|
|
// aspect. Any subresources that don't match the range, after the split, will fail to meet any
|
|
// of the handled cases, so we'll just continue processing.
|
|
if(it->subresourceRange.mipLevels == 1 &&
|
|
it->subresourceRange.arraySize == 1 &&
|
|
it->subresourceRange.baseMipLevel >= t.subresourceRange.baseMipLevel &&
|
|
it->subresourceRange.baseMipLevel < t.subresourceRange.baseMipLevel+nummips &&
|
|
it->subresourceRange.baseArrayLayer >= t.subresourceRange.baseArrayLayer &&
|
|
it->subresourceRange.baseArrayLayer < t.subresourceRange.baseArrayLayer+numslices)
|
|
{
|
|
// apply it (prevstate is from the start of all barriers accumulated, so only set once)
|
|
if(it->oldLayout == UNKNOWN_PREV_IMG_LAYOUT)
|
|
it->oldLayout = t.oldLayout;
|
|
it->newLayout = t.newLayout;
|
|
|
|
// continue as there might be more, but we're done
|
|
done = true;
|
|
continue;
|
|
}
|
|
// finally handle the case where we have a range that covers a whole image but we need to
|
|
// split it. If the barrier covered the whole image too it would have hit the very first
|
|
// case, so we know that the barrier doesn't cover the whole range.
|
|
// Also, if we've already done the split this case won't be hit and we'll either fall into
|
|
// the case above, or we'll finish as we've covered the whole barrier.
|
|
else if(it->subresourceRange.mipLevels > 1 || it->subresourceRange.arraySize > 1)
|
|
{
|
|
ImageRegionState existing = *it;
|
|
|
|
// remember where we were in the array, as after this iterators will be
|
|
// invalidated.
|
|
size_t offs = it - stit->second.subresourceStates.begin();
|
|
size_t count = it->subresourceRange.mipLevels * it->subresourceRange.arraySize;
|
|
|
|
// only insert count-1 as we want count entries total - one per subresource
|
|
stit->second.subresourceStates.insert(it, count-1, existing);
|
|
|
|
// it now points at the first subresource, but we need to modify the ranges
|
|
// to be valid
|
|
it = stit->second.subresourceStates.begin()+offs;
|
|
|
|
for(size_t i=0; i < count; i++)
|
|
{
|
|
it->subresourceRange.mipLevels = 1;
|
|
it->subresourceRange.arraySize = 1;
|
|
|
|
// slice-major
|
|
it->subresourceRange.baseArrayLayer = uint32_t(i / existing.subresourceRange.mipLevels);
|
|
it->subresourceRange.baseMipLevel = uint32_t(i % existing.subresourceRange.mipLevels);
|
|
it++;
|
|
}
|
|
|
|
// reset the iterator to point to the first subresource
|
|
it = stit->second.subresourceStates.begin()+offs;
|
|
|
|
// the loop will continue after this point and look at the next subresources
|
|
// so we need to check to see if the first subresource lies in the range here
|
|
if(it->subresourceRange.baseMipLevel >= t.subresourceRange.baseMipLevel &&
|
|
it->subresourceRange.baseMipLevel < t.subresourceRange.baseMipLevel+nummips &&
|
|
it->subresourceRange.baseArrayLayer >= t.subresourceRange.baseArrayLayer &&
|
|
it->subresourceRange.baseArrayLayer < t.subresourceRange.baseArrayLayer+numslices)
|
|
{
|
|
// apply it (prevstate is from the start of all barriers accumulated, so only set once)
|
|
if(it->oldLayout == UNKNOWN_PREV_IMG_LAYOUT)
|
|
it->oldLayout = t.oldLayout;
|
|
it->newLayout = t.newLayout;
|
|
|
|
// continue as there might be more, but we're done
|
|
done = true;
|
|
}
|
|
|
|
// continue processing from here
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we've gone past where the new subresource range would sit
|
|
if(it->subresourceRange.aspectMask > t.subresourceRange.aspectMask)
|
|
break;
|
|
|
|
// otherwise continue to try and find the subresource range
|
|
}
|
|
|
|
if(!done)
|
|
RDCERR("Couldn't find subresource range to apply barrier to - invalid!");
|
|
}
|
|
}
|
|
|
|
bool VulkanResourceManager::Force_InitialState(WrappedVkRes *res)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool VulkanResourceManager::Need_InitialStateChunk(WrappedVkRes *res)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool VulkanResourceManager::Prepare_InitialState(WrappedVkRes *res)
|
|
{
|
|
return m_Core->Prepare_InitialState(res);
|
|
}
|
|
|
|
bool VulkanResourceManager::Serialise_InitialState(WrappedVkRes *res)
|
|
{
|
|
return m_Core->Serialise_InitialState(res);
|
|
}
|
|
|
|
void VulkanResourceManager::Create_InitialState(ResourceId id, WrappedVkRes *live, bool hasData)
|
|
{
|
|
return m_Core->Create_InitialState(id, live, hasData);
|
|
}
|
|
|
|
void VulkanResourceManager::Apply_InitialState(WrappedVkRes *live, InitialContentData initial)
|
|
{
|
|
return m_Core->Apply_InitialState(live, initial);
|
|
}
|
|
|
|
bool VulkanResourceManager::ResourceTypeRelease(WrappedVkRes *res)
|
|
{
|
|
return m_Core->ReleaseResource(res);
|
|
}
|