mirror of
https://github.com/skyline-emu/skyline.git
synced 2025-01-17 17:57:54 +03:00
Lock TextureManager
/BufferManager
during submission
Multiple threads concurrently accessing the `TextureManager`/`BufferManager` (Referred to as "resource managers") has a potential deadlock with a resource being locked while acquiring the resource manager lock while the thread owning it tries to acquire a lock on the resource resulting in a deadlock. This has been fixed with locking of resource manager now being externally handled which ensures it can be locked prior to locking any resources, `CommandExecutor` provides accessors for retrieving the resource manager which automatically handles locking aside doing so on attachment of resources.
This commit is contained in:
parent
1239907ce8
commit
0ac5f4ce27
@ -12,6 +12,18 @@ namespace skyline::gpu {
|
|||||||
return it->guest->begin().base() < pointer;
|
return it->guest->begin().base() < pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferManager::lock() {
|
||||||
|
mutex.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferManager::unlock() {
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BufferManager::try_lock() {
|
||||||
|
return mutex.try_lock();
|
||||||
|
}
|
||||||
|
|
||||||
BufferView BufferManager::FindOrCreate(GuestBuffer guestMapping, ContextTag tag) {
|
BufferView BufferManager::FindOrCreate(GuestBuffer guestMapping, ContextTag tag) {
|
||||||
/*
|
/*
|
||||||
* We align the buffer to the page boundary to ensure that:
|
* We align the buffer to the page boundary to ensure that:
|
||||||
@ -22,8 +34,6 @@ namespace skyline::gpu {
|
|||||||
vk::DeviceSize offset{static_cast<size_t>(guestMapping.begin().base() - alignedStart)}, size{guestMapping.size()};
|
vk::DeviceSize offset{static_cast<size_t>(guestMapping.begin().base() - alignedStart)}, size{guestMapping.size()};
|
||||||
guestMapping = span<u8>{alignedStart, alignedEnd};
|
guestMapping = span<u8>{alignedStart, alignedEnd};
|
||||||
|
|
||||||
std::scoped_lock lock(mutex);
|
|
||||||
|
|
||||||
// Lookup for any buffers overlapping with the supplied guest mapping
|
// Lookup for any buffers overlapping with the supplied guest mapping
|
||||||
boost::container::small_vector<std::shared_ptr<Buffer>, 4> overlaps;
|
boost::container::small_vector<std::shared_ptr<Buffer>, 4> overlaps;
|
||||||
for (auto entryIt{std::lower_bound(buffers.begin(), buffers.end(), guestMapping.end().base(), BufferLessThan)}; entryIt != buffers.begin() && (*--entryIt)->guest->begin() <= guestMapping.end();)
|
for (auto entryIt{std::lower_bound(buffers.begin(), buffers.end(), guestMapping.end().base(), BufferLessThan)}; entryIt != buffers.begin() && (*--entryIt)->guest->begin() <= guestMapping.end();)
|
||||||
@ -89,7 +99,9 @@ namespace skyline::gpu {
|
|||||||
return newBuffer->GetView(static_cast<vk::DeviceSize>(guestMapping.begin() - newBuffer->guest->begin()) + offset, size);
|
return newBuffer->GetView(static_cast<vk::DeviceSize>(guestMapping.begin() - newBuffer->guest->begin()) + offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferManager::MegaBufferSlot::MegaBufferSlot(GPU &gpu) : backing(gpu.memory.AllocateBuffer(Size)) {}
|
constexpr static vk::DeviceSize MegaBufferSize{100 * 1024 * 1024}; //!< Size in bytes of the megabuffer (100MiB)
|
||||||
|
|
||||||
|
BufferManager::MegaBufferSlot::MegaBufferSlot(GPU &gpu) : backing(gpu.memory.AllocateBuffer(MegaBufferSize)) {}
|
||||||
|
|
||||||
MegaBuffer::MegaBuffer(BufferManager::MegaBufferSlot &slot) : slot{&slot}, freeRegion{slot.backing.subspan(PAGE_SIZE)} {}
|
MegaBuffer::MegaBuffer(BufferManager::MegaBufferSlot &slot) : slot{&slot}, freeRegion{slot.backing.subspan(PAGE_SIZE)} {}
|
||||||
|
|
||||||
@ -135,7 +147,7 @@ namespace skyline::gpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MegaBuffer BufferManager::AcquireMegaBuffer(const std::shared_ptr<FenceCycle> &cycle) {
|
MegaBuffer BufferManager::AcquireMegaBuffer(const std::shared_ptr<FenceCycle> &cycle) {
|
||||||
std::scoped_lock lock{mutex};
|
std::scoped_lock lock{megaBufferMutex};
|
||||||
|
|
||||||
for (auto &slot : megaBuffers) {
|
for (auto &slot : megaBuffers) {
|
||||||
if (!slot.active.test_and_set(std::memory_order_acq_rel)) {
|
if (!slot.active.test_and_set(std::memory_order_acq_rel)) {
|
||||||
|
@ -17,6 +17,8 @@ namespace skyline::gpu {
|
|||||||
std::mutex mutex; //!< Synchronizes access to the buffer mappings
|
std::mutex mutex; //!< Synchronizes access to the buffer mappings
|
||||||
std::vector<std::shared_ptr<Buffer>> buffers; //!< A sorted vector of all buffer mappings
|
std::vector<std::shared_ptr<Buffer>> buffers; //!< A sorted vector of all buffer mappings
|
||||||
|
|
||||||
|
std::mutex megaBufferMutex; //!< Synchronizes access to the allocated megabuffers
|
||||||
|
|
||||||
friend class MegaBuffer;
|
friend class MegaBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,7 +28,6 @@ namespace skyline::gpu {
|
|||||||
std::atomic_flag active{true}; //!< If the megabuffer is currently being utilized, we want to construct a buffer as active
|
std::atomic_flag active{true}; //!< If the megabuffer is currently being utilized, we want to construct a buffer as active
|
||||||
std::shared_ptr<FenceCycle> cycle; //!< The latest cycle on the fence, all waits must be performed through this
|
std::shared_ptr<FenceCycle> cycle; //!< The latest cycle on the fence, all waits must be performed through this
|
||||||
|
|
||||||
constexpr static vk::DeviceSize Size{100 * 1024 * 1024}; //!< Size in bytes of the megabuffer (100MiB)
|
|
||||||
memory::Buffer backing; //!< The GPU buffer as the backing storage for the megabuffer
|
memory::Buffer backing; //!< The GPU buffer as the backing storage for the megabuffer
|
||||||
|
|
||||||
MegaBufferSlot(GPU &gpu);
|
MegaBufferSlot(GPU &gpu);
|
||||||
@ -42,14 +43,34 @@ namespace skyline::gpu {
|
|||||||
|
|
||||||
BufferManager(GPU &gpu);
|
BufferManager(GPU &gpu);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Acquires an exclusive lock on the texture for the calling thread
|
||||||
|
* @note Naming is in accordance to the BasicLockable named requirement
|
||||||
|
*/
|
||||||
|
void lock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Relinquishes an existing lock on the texture by the calling thread
|
||||||
|
* @note Naming is in accordance to the BasicLockable named requirement
|
||||||
|
*/
|
||||||
|
void unlock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Attempts to acquire an exclusive lock but returns immediately if it's captured by another thread
|
||||||
|
* @note Naming is in accordance to the Lockable named requirement
|
||||||
|
*/
|
||||||
|
bool try_lock();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A pre-existing or newly created Buffer object which covers the supplied mappings
|
* @return A pre-existing or newly created Buffer object which covers the supplied mappings
|
||||||
|
* @note The buffer manager **must** be locked prior to calling this
|
||||||
*/
|
*/
|
||||||
BufferView FindOrCreate(GuestBuffer guestMapping, ContextTag tag = {});
|
BufferView FindOrCreate(GuestBuffer guestMapping, ContextTag tag = {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A dynamically allocated megabuffer which can be used to store buffer modifications allowing them to be replayed in-sequence on the GPU
|
* @return A dynamically allocated megabuffer which can be used to store buffer modifications allowing them to be replayed in-sequence on the GPU
|
||||||
* @note This object **must** be destroyed to be reclaimed by the manager and prevent a memory leak
|
* @note This object **must** be destroyed to be reclaimed by the manager and prevent a memory leak
|
||||||
|
* @note The buffer manager **doesn't** need to be locked prior to calling this
|
||||||
*/
|
*/
|
||||||
MegaBuffer AcquireMegaBuffer(const std::shared_ptr<FenceCycle> &cycle);
|
MegaBuffer AcquireMegaBuffer(const std::shared_ptr<FenceCycle> &cycle);
|
||||||
};
|
};
|
||||||
|
@ -118,11 +118,12 @@ namespace skyline::gpu::interconnect {
|
|||||||
auto srcGuestTexture{GetGuestTexture(srcSurface)};
|
auto srcGuestTexture{GetGuestTexture(srcSurface)};
|
||||||
auto dstGuestTexture{GetGuestTexture(dstSurface)};
|
auto dstGuestTexture{GetGuestTexture(dstSurface)};
|
||||||
|
|
||||||
auto srcTextureView{gpu.texture.FindOrCreate(srcGuestTexture, executor.tag)};
|
auto &textureManager{executor.AcquireTextureManager()};
|
||||||
executor.AttachTexture(&*srcTextureView);
|
auto srcTextureView{textureManager.FindOrCreate(srcGuestTexture, executor.tag)};
|
||||||
|
executor.AttachTexture(srcTextureView.get());
|
||||||
|
|
||||||
auto dstTextureView{gpu.texture.FindOrCreate(dstGuestTexture, executor.tag)};
|
auto dstTextureView{textureManager.FindOrCreate(dstGuestTexture, executor.tag)};
|
||||||
executor.AttachTexture(&*dstTextureView);
|
executor.AttachTexture(dstTextureView.get());
|
||||||
|
|
||||||
auto getSubresourceLayers{[](const vk::ImageSubresourceRange &range, vk::ImageAspectFlags aspect) {
|
auto getSubresourceLayers{[](const vk::ImageSubresourceRange &range, vk::ImageAspectFlags aspect) {
|
||||||
return vk::ImageSubresourceLayers{
|
return vk::ImageSubresourceLayers{
|
||||||
|
@ -11,6 +11,18 @@ namespace skyline::gpu::interconnect {
|
|||||||
cycle->Cancel();
|
cycle->Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextureManager &CommandExecutor::AcquireTextureManager() {
|
||||||
|
if (!textureManagerLock)
|
||||||
|
textureManagerLock.emplace(gpu.texture);
|
||||||
|
return gpu.texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferManager &CommandExecutor::AcquireBufferManager() {
|
||||||
|
if (!bufferManagerLock)
|
||||||
|
bufferManagerLock.emplace(gpu.buffer);
|
||||||
|
return gpu.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
bool CommandExecutor::CreateRenderPassWithSubpass(vk::Rect2D renderArea, span<TextureView *> inputAttachments, span<TextureView *> colorAttachments, TextureView *depthStencilAttachment) {
|
bool CommandExecutor::CreateRenderPassWithSubpass(vk::Rect2D renderArea, span<TextureView *> inputAttachments, span<TextureView *> colorAttachments, TextureView *depthStencilAttachment) {
|
||||||
auto addSubpass{[&] {
|
auto addSubpass{[&] {
|
||||||
renderPass->AddSubpass(inputAttachments, colorAttachments, depthStencilAttachment, gpu);
|
renderPass->AddSubpass(inputAttachments, colorAttachments, depthStencilAttachment, gpu);
|
||||||
@ -85,6 +97,10 @@ namespace skyline::gpu::interconnect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CommandExecutor::AttachTexture(TextureView *view) {
|
bool CommandExecutor::AttachTexture(TextureView *view) {
|
||||||
|
if (!textureManagerLock)
|
||||||
|
// Avoids a potential deadlock with this resource being locked while acquiring the TextureManager lock while the thread owning it tries to acquire a lock on this texture
|
||||||
|
textureManagerLock.emplace(gpu.texture);
|
||||||
|
|
||||||
bool didLock{view->LockWithTag(tag)};
|
bool didLock{view->LockWithTag(tag)};
|
||||||
if (didLock)
|
if (didLock)
|
||||||
attachedTextures.emplace_back(view->texture);
|
attachedTextures.emplace_back(view->texture);
|
||||||
@ -105,6 +121,10 @@ namespace skyline::gpu::interconnect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CommandExecutor::AttachBuffer(BufferView &view) {
|
bool CommandExecutor::AttachBuffer(BufferView &view) {
|
||||||
|
if (!bufferManagerLock)
|
||||||
|
// See AttachTexture(...)
|
||||||
|
bufferManagerLock.emplace(gpu.buffer);
|
||||||
|
|
||||||
bool didLock{view->LockWithTag(tag)};
|
bool didLock{view->LockWithTag(tag)};
|
||||||
if (didLock)
|
if (didLock)
|
||||||
attachedBuffers.emplace_back(view->buffer);
|
attachedBuffers.emplace_back(view->buffer);
|
||||||
|
@ -20,6 +20,9 @@ namespace skyline::gpu::interconnect {
|
|||||||
node::RenderPassNode *renderPass{};
|
node::RenderPassNode *renderPass{};
|
||||||
size_t subpassCount{}; //!< The number of subpasses in the current render pass
|
size_t subpassCount{}; //!< The number of subpasses in the current render pass
|
||||||
|
|
||||||
|
std::optional<std::scoped_lock<TextureManager>> textureManagerLock; //!< The lock on the texture manager, this is locked for the duration of the command execution from the first usage inside an execution to the submission
|
||||||
|
std::optional<std::scoped_lock<BufferManager>> bufferManagerLock; //!< The lock on the buffer manager, this is locked for the duration of the command execution from the first usage inside an execution to the submission
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A wrapper of a Texture object that has been locked beforehand and must be unlocked afterwards
|
* @brief A wrapper of a Texture object that has been locked beforehand and must be unlocked afterwards
|
||||||
*/
|
*/
|
||||||
@ -95,6 +98,12 @@ namespace skyline::gpu::interconnect {
|
|||||||
|
|
||||||
~CommandExecutor();
|
~CommandExecutor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A reference to an instance of the Texture Manager which will be locked till execution
|
||||||
|
* @note Any access to the texture manager while recording commands **must** be done via this
|
||||||
|
*/
|
||||||
|
TextureManager &AcquireTextureManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Attach the lifetime of the texture to the command buffer
|
* @brief Attach the lifetime of the texture to the command buffer
|
||||||
* @return If this is the first usage of the backing of this resource within this execution
|
* @return If this is the first usage of the backing of this resource within this execution
|
||||||
@ -103,6 +112,12 @@ namespace skyline::gpu::interconnect {
|
|||||||
*/
|
*/
|
||||||
bool AttachTexture(TextureView *view);
|
bool AttachTexture(TextureView *view);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A reference to an instance of the Buffer Manager which will be locked till execution
|
||||||
|
* @note Any access to the buffer manager while recording commands **must** be done via this
|
||||||
|
*/
|
||||||
|
BufferManager &AcquireBufferManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Attach the lifetime of a buffer to the command buffer
|
* @brief Attach the lifetime of a buffer to the command buffer
|
||||||
* @return If this is the first usage of the backing of this resource within this execution
|
* @return If this is the first usage of the backing of this resource within this execution
|
||||||
|
@ -400,7 +400,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
return vk::ImageViewType::e2D;
|
return vk::ImageViewType::e2D;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest, executor.tag);
|
renderTarget.view = executor.AcquireTextureManager().FindOrCreate(renderTarget.guest, executor.tag);
|
||||||
return renderTarget.view.get();
|
return renderTarget.view.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,7 +725,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
auto view{constantBufferCache.Lookup(constantBufferSelector.size, constantBufferSelector.iova)};
|
auto view{constantBufferCache.Lookup(constantBufferSelector.size, constantBufferSelector.iova)};
|
||||||
if (!view) {
|
if (!view) {
|
||||||
auto mappings{channelCtx.asCtx->gmmu.TranslateRange(constantBufferSelector.iova, constantBufferSelector.size)};
|
auto mappings{channelCtx.asCtx->gmmu.TranslateRange(constantBufferSelector.iova, constantBufferSelector.size)};
|
||||||
view = gpu.buffer.FindOrCreate(mappings.front(), executor.tag);
|
view = executor.AcquireBufferManager().FindOrCreate(mappings.front(), executor.tag);
|
||||||
constantBufferCache.Insert(constantBufferSelector.size, constantBufferSelector.iova, *view);
|
constantBufferCache.Insert(constantBufferSelector.size, constantBufferSelector.iova, *view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,7 +916,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
if (mappings.size() != 1)
|
if (mappings.size() != 1)
|
||||||
Logger::Warn("Multiple buffer mappings ({}) are not supported", mappings.size());
|
Logger::Warn("Multiple buffer mappings ({}) are not supported", mappings.size());
|
||||||
|
|
||||||
return gpu.buffer.FindOrCreate(mappings.front(), executor.tag);
|
return executor.AcquireBufferManager().FindOrCreate(mappings.front(), executor.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1845,7 +1845,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
if (mappings.size() != 1)
|
if (mappings.size() != 1)
|
||||||
Logger::Warn("Multiple buffer mappings ({}) are not supported", mappings.size());
|
Logger::Warn("Multiple buffer mappings ({}) are not supported", mappings.size());
|
||||||
|
|
||||||
vertexBuffer.view = gpu.buffer.FindOrCreate(mappings.front(), executor.tag);
|
vertexBuffer.view = executor.AcquireBufferManager().FindOrCreate(mappings.front(), executor.tag);
|
||||||
return &vertexBuffer;
|
return &vertexBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2329,7 +2329,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
return textureView;
|
return textureView;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto textureView{gpu.texture.FindOrCreate(poolTexture.guest, executor.tag)};
|
auto textureView{executor.AcquireTextureManager().FindOrCreate(poolTexture.guest, executor.tag)};
|
||||||
poolTexture.view = textureView;
|
poolTexture.view = textureView;
|
||||||
return textureView;
|
return textureView;
|
||||||
}
|
}
|
||||||
@ -2600,7 +2600,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
Logger::Warn("Multiple buffer mappings ({}) are not supported", mappings.size());
|
Logger::Warn("Multiple buffer mappings ({}) are not supported", mappings.size());
|
||||||
|
|
||||||
auto mapping{mappings.front()};
|
auto mapping{mappings.front()};
|
||||||
indexBuffer.view = gpu.buffer.FindOrCreate(span<u8>(mapping.data(), size), executor.tag);
|
indexBuffer.view = executor.AcquireBufferManager().FindOrCreate(span<u8>(mapping.data(), size), executor.tag);
|
||||||
return indexBuffer.view;
|
return indexBuffer.view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,18 @@
|
|||||||
namespace skyline::gpu {
|
namespace skyline::gpu {
|
||||||
TextureManager::TextureManager(GPU &gpu) : gpu(gpu) {}
|
TextureManager::TextureManager(GPU &gpu) : gpu(gpu) {}
|
||||||
|
|
||||||
|
void TextureManager::lock() {
|
||||||
|
mutex.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureManager::unlock() {
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureManager::try_lock() {
|
||||||
|
return mutex.try_lock();
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<TextureView> TextureManager::FindOrCreate(const GuestTexture &guestTexture, ContextTag tag) {
|
std::shared_ptr<TextureView> TextureManager::FindOrCreate(const GuestTexture &guestTexture, ContextTag tag) {
|
||||||
auto guestMapping{guestTexture.mappings.front()};
|
auto guestMapping{guestTexture.mappings.front()};
|
||||||
|
|
||||||
@ -23,7 +35,6 @@ namespace skyline::gpu {
|
|||||||
* 5) Create a new texture and insert it in the map then return it
|
* 5) Create a new texture and insert it in the map then return it
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::scoped_lock lock(mutex);
|
|
||||||
std::shared_ptr<Texture> match{};
|
std::shared_ptr<Texture> match{};
|
||||||
auto mappingEnd{std::upper_bound(textures.begin(), textures.end(), guestMapping)}, hostMapping{mappingEnd};
|
auto mappingEnd{std::upper_bound(textures.begin(), textures.end(), guestMapping)}, hostMapping{mappingEnd};
|
||||||
while (hostMapping != textures.begin() && (--hostMapping)->end() > guestMapping.begin()) {
|
while (hostMapping != textures.begin() && (--hostMapping)->end() > guestMapping.begin()) {
|
||||||
|
@ -32,8 +32,27 @@ namespace skyline::gpu {
|
|||||||
public:
|
public:
|
||||||
TextureManager(GPU &gpu);
|
TextureManager(GPU &gpu);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Acquires an exclusive lock on the texture for the calling thread
|
||||||
|
* @note Naming is in accordance to the BasicLockable named requirement
|
||||||
|
*/
|
||||||
|
void lock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Relinquishes an existing lock on the texture by the calling thread
|
||||||
|
* @note Naming is in accordance to the BasicLockable named requirement
|
||||||
|
*/
|
||||||
|
void unlock();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Attempts to acquire an exclusive lock but returns immediately if it's captured by another thread
|
||||||
|
* @note Naming is in accordance to the Lockable named requirement
|
||||||
|
*/
|
||||||
|
bool try_lock();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A pre-existing or newly created Texture object which matches the specified criteria
|
* @return A pre-existing or newly created Texture object which matches the specified criteria
|
||||||
|
* @note The texture manager **must** be locked prior to calling this
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<TextureView> FindOrCreate(const GuestTexture &guestTexture, ContextTag tag = {});
|
std::shared_ptr<TextureView> FindOrCreate(const GuestTexture &guestTexture, ContextTag tag = {});
|
||||||
};
|
};
|
||||||
|
@ -347,6 +347,8 @@ namespace skyline::service::hosbinder {
|
|||||||
gpu::texture::Dimensions dimensions(surface.width, surface.height);
|
gpu::texture::Dimensions dimensions(surface.width, surface.height);
|
||||||
gpu::GuestTexture guestTexture(span<u8>{}, dimensions, format, tileConfig, vk::ImageViewType::e2D);
|
gpu::GuestTexture guestTexture(span<u8>{}, dimensions, format, tileConfig, vk::ImageViewType::e2D);
|
||||||
guestTexture.mappings[0] = span<u8>(nvMapHandleObj->GetPointer() + surface.offset, guestTexture.GetLayerStride());
|
guestTexture.mappings[0] = span<u8>(nvMapHandleObj->GetPointer() + surface.offset, guestTexture.GetLayerStride());
|
||||||
|
|
||||||
|
std::scoped_lock textureLock{state.gpu->texture};
|
||||||
buffer.texture = state.gpu->texture.FindOrCreate(guestTexture)->texture;
|
buffer.texture = state.gpu->texture.FindOrCreate(guestTexture)->texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user