vulkan: memory allocator
This commit is contained in:
parent
6304b1e9c1
commit
06e1456d4f
|
@ -450,7 +450,9 @@ void* rend_thread(void* p)
|
|||
|
||||
bool pend_rend = false;
|
||||
|
||||
void rend_resize(int width, int height) {
|
||||
void rend_resize(int width, int height)
|
||||
{
|
||||
if (renderer != nullptr)
|
||||
renderer->Resize(width, height);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ void *rend_thread(void *);
|
|||
|
||||
void rend_set_fb_scale(float x,float y);
|
||||
void rend_resize(int width, int height);
|
||||
void rend_text_invl(vram_block* bl);
|
||||
|
||||
///////
|
||||
extern TA_context* _pvrrc;
|
||||
|
|
|
@ -479,7 +479,7 @@ bool BaseTextureCacheData::Delete()
|
|||
|
||||
if (lock_block)
|
||||
libCore_vramlock_Unlock_block(lock_block);
|
||||
lock_block=0;
|
||||
lock_block = nullptr;
|
||||
|
||||
delete[] custom_image_data;
|
||||
|
||||
|
@ -494,6 +494,7 @@ void BaseTextureCacheData::Create()
|
|||
dirty = FrameCount;
|
||||
lock_block = nullptr;
|
||||
custom_image_data = nullptr;
|
||||
custom_load_in_progress = 0;
|
||||
|
||||
//decode info from tsp/tcw into the texture struct
|
||||
tex = &format[tcw.PixelFmt == PixelReserved ? Pixel1555 : tcw.PixelFmt]; //texture format table entry
|
||||
|
@ -731,47 +732,7 @@ void BaseTextureCacheData::CheckCustomTexture()
|
|||
}
|
||||
}
|
||||
|
||||
static std::unordered_map<u64, std::unique_ptr<BaseTextureCacheData>> TexCache;
|
||||
typedef std::unordered_map<u64, std::unique_ptr<BaseTextureCacheData>>::iterator TexCacheIter;
|
||||
|
||||
// Only use TexU and TexV from TSP in the cache key
|
||||
// TexV : 7, TexU : 7
|
||||
static const TSP TSPTextureCacheMask = { { 7, 7 } };
|
||||
// TexAddr : 0x1FFFFF, Reserved : 0, StrideSel : 0, ScanOrder : 1, PixelFmt : 7, VQ_Comp : 1, MipMapped : 1
|
||||
static const TCW TCWTextureCacheMask = { { 0x1FFFFF, 0, 0, 1, 7, 1, 1 } };
|
||||
|
||||
BaseTextureCacheData *getTextureCacheData(TSP tsp, TCW tcw, BaseTextureCacheData *(*factory)())
|
||||
{
|
||||
u64 key = tsp.full & TSPTextureCacheMask.full;
|
||||
if (tcw.PixelFmt == PixelPal4 || tcw.PixelFmt == PixelPal8)
|
||||
// Paletted textures have a palette selection that must be part of the key
|
||||
// We also add the palette type to the key to avoid thrashing the cache
|
||||
// when the palette type is changed. If the palette type is changed back in the future,
|
||||
// this texture will stil be available.
|
||||
key |= ((u64)tcw.full << 32) | ((PAL_RAM_CTRL & 3) << 6);
|
||||
else
|
||||
key |= (u64)(tcw.full & TCWTextureCacheMask.full) << 32;
|
||||
|
||||
TexCacheIter tx = TexCache.find(key);
|
||||
|
||||
BaseTextureCacheData* tf;
|
||||
if (tx != TexCache.end())
|
||||
{
|
||||
tf = tx->second.get();
|
||||
// Needed if the texture is updated
|
||||
tf->tcw.StrideSel = tcw.StrideSel;
|
||||
}
|
||||
else //create if not existing
|
||||
{
|
||||
tf = factory();
|
||||
TexCache[key] = std::unique_ptr<BaseTextureCacheData>(tf);
|
||||
|
||||
tf->tsp = tsp;
|
||||
tf->tcw = tcw;
|
||||
}
|
||||
|
||||
return tf;
|
||||
}
|
||||
std::unordered_map<u64, std::unique_ptr<BaseTextureCacheData>> TexCache;
|
||||
|
||||
void CollectCleanup()
|
||||
{
|
||||
|
@ -788,14 +749,12 @@ void CollectCleanup()
|
|||
break;
|
||||
}
|
||||
|
||||
for (u64 id : list) {
|
||||
if (TexCache[id]->Delete())
|
||||
for (u64 id : list)
|
||||
{
|
||||
//printf("Deleting %d\n", TexCache[list[i]].texID);
|
||||
if (TexCache[id]->Delete())
|
||||
TexCache.erase(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void killtex()
|
||||
{
|
||||
|
@ -974,5 +933,13 @@ void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst)
|
|||
}
|
||||
dst += (stride - width * 2) / 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void rend_text_invl(vram_block* bl)
|
||||
{
|
||||
BaseTextureCacheData* tcd = (BaseTextureCacheData*)bl->userdata;
|
||||
tcd->dirty = FrameCount;
|
||||
tcd->lock_block = nullptr;
|
||||
|
||||
libCore_vramlock_Unlock_block_wb(bl);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "oslib/oslib.h"
|
||||
#include "hw/pvr/pvr_regs.h"
|
||||
#include "hw/pvr/ta_structs.h"
|
||||
|
@ -664,8 +666,8 @@ struct BaseTextureCacheData
|
|||
u32 texture_hash; // xxhash of texture data, used for custom textures
|
||||
u32 old_texture_hash; // legacy hash
|
||||
u8* custom_image_data; // loaded custom image data
|
||||
volatile u32 custom_width;
|
||||
volatile u32 custom_height;
|
||||
u32 custom_width;
|
||||
u32 custom_height;
|
||||
std::atomic_int custom_load_in_progress;
|
||||
|
||||
void PrintTextureName();
|
||||
|
@ -701,9 +703,54 @@ struct BaseTextureCacheData
|
|||
virtual bool Delete();
|
||||
virtual ~BaseTextureCacheData() {}
|
||||
};
|
||||
BaseTextureCacheData *getTextureCacheData(TSP tsp, TCW tcw, BaseTextureCacheData *(*factory)());
|
||||
|
||||
extern std::unordered_map<u64, std::unique_ptr<BaseTextureCacheData>> TexCache;
|
||||
typedef std::unordered_map<u64, std::unique_ptr<BaseTextureCacheData>>::iterator TexCacheIter;
|
||||
|
||||
// Only use TexU and TexV from TSP in the cache key
|
||||
// TexV : 7, TexU : 7
|
||||
const TSP TSPTextureCacheMask = { { 7, 7 } };
|
||||
// TexAddr : 0x1FFFFF, Reserved : 0, StrideSel : 0, ScanOrder : 1, PixelFmt : 7, VQ_Comp : 1, MipMapped : 1
|
||||
const TCW TCWTextureCacheMask = { { 0x1FFFFF, 0, 0, 1, 7, 1, 1 } };
|
||||
|
||||
template<typename Func>
|
||||
BaseTextureCacheData *getTextureCacheData(TSP tsp, TCW tcw, Func factory)
|
||||
{
|
||||
u64 key = tsp.full & TSPTextureCacheMask.full;
|
||||
if (tcw.PixelFmt == PixelPal4 || tcw.PixelFmt == PixelPal8)
|
||||
// Paletted textures have a palette selection that must be part of the key
|
||||
// We also add the palette type to the key to avoid thrashing the cache
|
||||
// when the palette type is changed. If the palette type is changed back in the future,
|
||||
// this texture will stil be available.
|
||||
key |= ((u64)tcw.full << 32) | ((PAL_RAM_CTRL & 3) << 6);
|
||||
else
|
||||
key |= (u64)(tcw.full & TCWTextureCacheMask.full) << 32;
|
||||
|
||||
TexCacheIter it = TexCache.find(key);
|
||||
|
||||
BaseTextureCacheData* texture;
|
||||
if (it != TexCache.end())
|
||||
{
|
||||
texture = it->second.get();
|
||||
// Needed if the texture is updated
|
||||
texture->tcw.StrideSel = tcw.StrideSel;
|
||||
}
|
||||
else //create if not existing
|
||||
{
|
||||
texture = factory();
|
||||
TexCache[key] = std::unique_ptr<BaseTextureCacheData>(texture);
|
||||
|
||||
texture->tsp = tsp;
|
||||
texture->tcw = tcw;
|
||||
}
|
||||
texture->Lookups++;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
void CollectCleanup();
|
||||
void killtex();
|
||||
void rend_text_invl(vram_block* bl);
|
||||
|
||||
void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height);
|
||||
void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst);
|
||||
|
|
|
@ -369,9 +369,6 @@ u64 gl_GetTexture(TSP tsp, TCW tcw)
|
|||
// TexCacheHits = 0;
|
||||
// }
|
||||
|
||||
//update state for opts/stuff
|
||||
tf->Lookups++;
|
||||
|
||||
//return gl texture
|
||||
return tf->texID;
|
||||
}
|
||||
|
@ -391,9 +388,6 @@ text_info raw_GetTexture(TSP tsp, TCW tcw)
|
|||
if (tf->NeedsUpdate())
|
||||
tf->Update();
|
||||
|
||||
//update state for opts/stuff
|
||||
tf->Lookups++;
|
||||
|
||||
//return gl texture
|
||||
rv.height = tf->h;
|
||||
rv.width = tf->w;
|
||||
|
@ -408,15 +402,6 @@ void DoCleanup() {
|
|||
|
||||
}
|
||||
|
||||
void rend_text_invl(vram_block* bl)
|
||||
{
|
||||
TextureCacheData* tcd = (TextureCacheData*)bl->userdata;
|
||||
tcd->dirty=FrameCount;
|
||||
tcd->lock_block=0;
|
||||
|
||||
libCore_vramlock_Unlock_block_wb(bl);
|
||||
}
|
||||
|
||||
GLuint fbTextureId;
|
||||
|
||||
void RenderFramebuffer()
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#include "allocator.h"
|
||||
|
||||
SimpleAllocator SimpleAllocator::instance;
|
|
@ -0,0 +1,199 @@
|
|||
#pragma once
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include "vulkan.h"
|
||||
#include "utils.h"
|
||||
|
||||
class VulkanAllocator;
|
||||
|
||||
// Manages a large chunk of memory using buddy memory allocation algorithm
|
||||
class Chunk
|
||||
{
|
||||
public:
|
||||
Chunk(vk::DeviceSize size)
|
||||
{
|
||||
verify(size >= SmallestBlockSize);
|
||||
freeBlocks.push_back(std::make_pair(0, PowerOf2(size)));
|
||||
}
|
||||
|
||||
private:
|
||||
vk::DeviceSize PowerOf2(vk::DeviceSize size)
|
||||
{
|
||||
vk::DeviceSize alignedSize = SmallestBlockSize;
|
||||
while (alignedSize < size)
|
||||
alignedSize *= 2;
|
||||
return alignedSize;
|
||||
}
|
||||
|
||||
vk::DeviceSize Allocate(vk::DeviceSize size, vk::DeviceSize alignment)
|
||||
{
|
||||
alignment--;
|
||||
const vk::DeviceSize alignedSize = PowerOf2(size);
|
||||
|
||||
auto smallestFreeBlock = freeBlocks.end();
|
||||
for (auto it = freeBlocks.begin(); it != freeBlocks.end(); it++)
|
||||
{
|
||||
if (it->second == alignedSize && (it->first & alignment) == 0)
|
||||
{
|
||||
// Free block of the right size and alignment found -> return it
|
||||
usedBlocks.insert(*it);
|
||||
vk::DeviceSize offset = it->first;
|
||||
freeBlocks.erase(it);
|
||||
|
||||
return offset;
|
||||
}
|
||||
if (it->second > alignedSize && (it->first & alignment) == 0
|
||||
&& (smallestFreeBlock == freeBlocks.end() || smallestFreeBlock->second > it->second))
|
||||
smallestFreeBlock = it;
|
||||
}
|
||||
if (smallestFreeBlock == freeBlocks.end())
|
||||
return OutOfMemory;
|
||||
|
||||
// We need to split larger blocks until we have one of the required size
|
||||
vk::DeviceSize offset = smallestFreeBlock->first;
|
||||
smallestFreeBlock->second /= 2;
|
||||
smallestFreeBlock->first += smallestFreeBlock->second;
|
||||
while (smallestFreeBlock->second > alignedSize)
|
||||
{
|
||||
freeBlocks.push_front(std::make_pair(offset + smallestFreeBlock->second / 2, smallestFreeBlock->second / 2));
|
||||
smallestFreeBlock = freeBlocks.begin();
|
||||
}
|
||||
usedBlocks[offset] = alignedSize;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Free(vk::DeviceSize offset)
|
||||
{
|
||||
auto usedBlock = usedBlocks.find(offset);
|
||||
verify(usedBlock != usedBlocks.end());
|
||||
vk::DeviceSize buddyOffset = offset ^ usedBlock->second;
|
||||
vk::DeviceSize buddySize = usedBlock->second;
|
||||
auto buddy = freeBlocks.end();
|
||||
while (true)
|
||||
{
|
||||
auto it = freeBlocks.begin();
|
||||
for (; it != freeBlocks.end(); it++)
|
||||
{
|
||||
if (it->first == buddyOffset && it->second == buddySize)
|
||||
{
|
||||
it->second *= 2;
|
||||
it->first &= ~(it->second - 1);
|
||||
if (buddy != freeBlocks.end())
|
||||
freeBlocks.erase(buddy);
|
||||
buddy = it;
|
||||
buddyOffset = it->first ^ buddy->second;
|
||||
buddySize = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buddy == freeBlocks.end())
|
||||
{
|
||||
// Initial order buddy not found -> add block to free list
|
||||
freeBlocks.push_front(std::make_pair(offset, usedBlock->second));
|
||||
break;
|
||||
}
|
||||
if (it == freeBlocks.end())
|
||||
break;
|
||||
}
|
||||
usedBlocks.erase(usedBlock);
|
||||
}
|
||||
|
||||
// first object/key is offset, second one/value is size
|
||||
std::list<std::pair<vk::DeviceSize, vk::DeviceSize>> freeBlocks;
|
||||
std::unordered_map<vk::DeviceSize, vk::DeviceSize> usedBlocks;
|
||||
vk::UniqueDeviceMemory deviceMemory;
|
||||
|
||||
static const vk::DeviceSize OutOfMemory = (vk::DeviceSize)-1;
|
||||
static const vk::DeviceSize SmallestBlockSize = 256;
|
||||
|
||||
friend class VulkanAllocator;
|
||||
};
|
||||
|
||||
class Allocator
|
||||
{
|
||||
public:
|
||||
virtual ~Allocator() {}
|
||||
virtual vk::DeviceSize Allocate(vk::DeviceSize size, vk::DeviceSize alignment, u32 memoryType, vk::DeviceMemory& deviceMemory) = 0;
|
||||
virtual void Free(vk::DeviceSize offset, u32 memoryType, vk::DeviceMemory deviceMemory) = 0;
|
||||
};
|
||||
|
||||
class VulkanAllocator : public Allocator
|
||||
{
|
||||
public:
|
||||
vk::DeviceSize Allocate(vk::DeviceSize size, vk::DeviceSize alignment, u32 memoryType, vk::DeviceMemory& deviceMemory) override
|
||||
{
|
||||
std::vector<Chunk>& chunks = findChunks(memoryType);
|
||||
for (auto& chunk : chunks)
|
||||
{
|
||||
vk::DeviceSize offset = chunk.Allocate(size, alignment);
|
||||
if (offset != Chunk::OutOfMemory)
|
||||
{
|
||||
deviceMemory = *chunk.deviceMemory;
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
const vk::DeviceSize newChunkSize = std::max(chunkSize, size);
|
||||
chunks.emplace_back(newChunkSize);
|
||||
Chunk& chunk = chunks.back();
|
||||
chunk.deviceMemory = VulkanContext::Instance()->GetDevice()->allocateMemoryUnique(vk::MemoryAllocateInfo(newChunkSize, memoryType));
|
||||
vk::DeviceSize offset = chunk.Allocate(size, alignment);
|
||||
verify(offset != Chunk::OutOfMemory);
|
||||
|
||||
deviceMemory = *chunk.deviceMemory;
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Free(vk::DeviceSize offset, u32 memoryType, vk::DeviceMemory deviceMemory) override
|
||||
{
|
||||
std::vector<Chunk>& chunks = findChunks(memoryType);
|
||||
for (auto chunkIt = chunks.begin(); chunkIt < chunks.end(); chunkIt++)
|
||||
{
|
||||
if (*chunkIt->deviceMemory == deviceMemory)
|
||||
{
|
||||
chunkIt->Free(offset);
|
||||
if (chunks.size() > 1 && chunkIt->usedBlocks.empty())
|
||||
{
|
||||
chunks.erase(chunkIt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
die("Invalid free");
|
||||
}
|
||||
|
||||
void SetChunkSize(vk::DeviceSize chunkSize) {
|
||||
this->chunkSize = chunkSize;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Chunk>& findChunks(u32 memoryType)
|
||||
{
|
||||
for (auto& pair : chunksPerMemType)
|
||||
if (pair.first == memoryType)
|
||||
return pair.second;
|
||||
chunksPerMemType.push_back(std::make_pair(memoryType, std::vector<Chunk>()));
|
||||
return chunksPerMemType.back().second;
|
||||
}
|
||||
|
||||
vk::DeviceSize chunkSize;
|
||||
std::vector<std::pair<u32, std::vector<Chunk>>> chunksPerMemType;
|
||||
};
|
||||
|
||||
class SimpleAllocator : public Allocator
|
||||
{
|
||||
public:
|
||||
vk::DeviceSize Allocate(vk::DeviceSize size, vk::DeviceSize alignment, u32 memoryType, vk::DeviceMemory& deviceMemory) override
|
||||
{
|
||||
deviceMemory = VulkanContext::Instance()->GetDevice()->allocateMemory(vk::MemoryAllocateInfo(size, memoryType));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Free(vk::DeviceSize offset, u32 memoryType, vk::DeviceMemory deviceMemory) override
|
||||
{
|
||||
VulkanContext::Instance()->GetDevice()->free(deviceMemory);
|
||||
}
|
||||
|
||||
static SimpleAllocator instance;
|
||||
};
|
|
@ -22,13 +22,15 @@
|
|||
#include "utils.h"
|
||||
|
||||
BufferData::BufferData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, vk::DeviceSize size,
|
||||
vk::BufferUsageFlags usage, vk::MemoryPropertyFlags propertyFlags)
|
||||
: m_size(size)
|
||||
vk::BufferUsageFlags usage, Allocator *allocator, vk::MemoryPropertyFlags propertyFlags)
|
||||
: device(device), bufferSize(size), allocator(allocator)
|
||||
#if !defined(NDEBUG)
|
||||
, m_usage(usage), m_propertyFlags(propertyFlags)
|
||||
#endif
|
||||
{
|
||||
buffer = device.createBufferUnique(vk::BufferCreateInfo(vk::BufferCreateFlags(), size, usage));
|
||||
deviceMemory = allocateMemory(device, physicalDevice.getMemoryProperties(), device.getBufferMemoryRequirements(buffer.get()), propertyFlags);
|
||||
device.bindBufferMemory(buffer.get(), deviceMemory.get(), 0);
|
||||
vk::MemoryRequirements memoryRequirements = device.getBufferMemoryRequirements(buffer.get());
|
||||
memoryType = findMemoryType(physicalDevice.getMemoryProperties(), memoryRequirements.memoryTypeBits, propertyFlags);
|
||||
offset = allocator->Allocate(memoryRequirements.size, memoryRequirements.alignment, memoryType, sharedDeviceMemory);
|
||||
device.bindBufferMemory(buffer.get(), sharedDeviceMemory, offset);
|
||||
}
|
||||
|
|
|
@ -20,52 +20,72 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include "vulkan.h"
|
||||
#include "allocator.h"
|
||||
|
||||
struct BufferData
|
||||
{
|
||||
BufferData(vk::PhysicalDevice const& physicalDevice, vk::Device const& device, vk::DeviceSize size, vk::BufferUsageFlags usage,
|
||||
Allocator *allocator = &SimpleAllocator::instance,
|
||||
vk::MemoryPropertyFlags propertyFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
|
||||
void upload(vk::Device const& device, u32 size, const void *data, u32 offset = 0) const
|
||||
~BufferData()
|
||||
{
|
||||
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
verify(offset + size <= m_size);
|
||||
|
||||
void* dataPtr = device.mapMemory(*this->deviceMemory, offset, size);
|
||||
memcpy(dataPtr, data, size);
|
||||
device.unmapMemory(*this->deviceMemory);
|
||||
buffer.reset();
|
||||
allocator->Free(offset, memoryType, sharedDeviceMemory);
|
||||
}
|
||||
|
||||
void upload(vk::Device const& device, size_t count, u32 *sizes, const void **data, u32 offset = 0) const
|
||||
void upload(vk::Device const& device, u32 size, const void *data, u32 bufOffset = 0) const
|
||||
{
|
||||
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
verify(offset + bufOffset + size <= bufferSize);
|
||||
|
||||
void* dataPtr = device.mapMemory(sharedDeviceMemory, offset + bufOffset, size);
|
||||
memcpy(dataPtr, data, size);
|
||||
device.unmapMemory(sharedDeviceMemory);
|
||||
}
|
||||
|
||||
void upload(vk::Device const& device, size_t count, u32 *sizes, const void **data, u32 bufOffset = 0) const
|
||||
{
|
||||
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
|
||||
u32 totalSize = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
totalSize += sizes[i];
|
||||
verify(offset + totalSize <= m_size);
|
||||
void* dataPtr = device.mapMemory(*this->deviceMemory, offset, totalSize);
|
||||
verify(offset + bufOffset + totalSize <= bufferSize);
|
||||
void* dataPtr = device.mapMemory(sharedDeviceMemory, offset + bufOffset, totalSize);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
memcpy(dataPtr, data[i], sizes[i]);
|
||||
dataPtr = (u8 *)dataPtr + sizes[i];
|
||||
}
|
||||
device.unmapMemory(*this->deviceMemory);
|
||||
device.unmapMemory(sharedDeviceMemory);
|
||||
}
|
||||
|
||||
void download(vk::Device const& device, u32 size, void *data, u32 offset = 0) const
|
||||
void download(vk::Device const& device, u32 size, void *data, u32 bufOffset = 0) const
|
||||
{
|
||||
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
|
||||
verify(offset + size <= m_size);
|
||||
verify(offset + bufOffset + size <= bufferSize);
|
||||
|
||||
void* dataPtr = device.mapMemory(*this->deviceMemory, offset, size);
|
||||
void* dataPtr = device.mapMemory(sharedDeviceMemory, offset + bufOffset, size);
|
||||
memcpy(data, dataPtr, size);
|
||||
device.unmapMemory(*this->deviceMemory);
|
||||
device.unmapMemory(sharedDeviceMemory);
|
||||
}
|
||||
|
||||
void *MapMemory()
|
||||
{
|
||||
return device.mapMemory(sharedDeviceMemory, offset, bufferSize);
|
||||
}
|
||||
void UnmapMemory()
|
||||
{
|
||||
device.unmapMemory(sharedDeviceMemory);
|
||||
}
|
||||
|
||||
vk::UniqueDeviceMemory deviceMemory;
|
||||
vk::UniqueBuffer buffer;
|
||||
vk::DeviceSize m_size;
|
||||
vk::DeviceSize bufferSize;
|
||||
Allocator *allocator;
|
||||
vk::DeviceSize offset;
|
||||
u32 memoryType;
|
||||
vk::DeviceMemory sharedDeviceMemory;
|
||||
vk::Device device;
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
private:
|
||||
|
|
|
@ -494,8 +494,8 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
|
|||
for (tsp.TexU = 0; tsp.TexU <= 7 && (8 << tsp.TexU) < origWidth; tsp.TexU++);
|
||||
for (tsp.TexV = 0; tsp.TexV <= 7 && (8 << tsp.TexV) < origHeight; tsp.TexV++);
|
||||
|
||||
texture = static_cast<Texture*>(getTextureCacheData(tsp, tcw, [](){
|
||||
return (BaseTextureCacheData *)new Texture(VulkanContext::Instance()->GetPhysicalDevice(), *VulkanContext::Instance()->GetDevice());
|
||||
texture = static_cast<Texture*>(getTextureCacheData(tsp, tcw, [this](){
|
||||
return (BaseTextureCacheData *)new Texture(VulkanContext::Instance()->GetPhysicalDevice(), *VulkanContext::Instance()->GetDevice(), this->texAllocator);
|
||||
}));
|
||||
if (texture->IsNew())
|
||||
texture->Create();
|
||||
|
@ -600,9 +600,6 @@ void TextureDrawer::EndRenderPass()
|
|||
}
|
||||
//memset(&vram[fb_rtt.TexAddr << 3], '\0', size);
|
||||
|
||||
if (width > 1024 || height > 1024)
|
||||
return;
|
||||
|
||||
texture->dirty = 0;
|
||||
if (texture->lock_block == NULL)
|
||||
texture->lock_block = libCore_vramlock_Lock(texture->sa_tex, texture->sa + texture->size - 1, texture);
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
bool Draw(const Texture *fogTexture);
|
||||
|
||||
protected:
|
||||
virtual void Init(SamplerManager *samplerManager, ShaderManager *shaderManager)
|
||||
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager)
|
||||
{
|
||||
this->samplerManager = samplerManager;
|
||||
pipelineManager->Init(shaderManager);
|
||||
|
@ -72,11 +72,15 @@ private:
|
|||
class ScreenDrawer : public Drawer
|
||||
{
|
||||
public:
|
||||
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager) override
|
||||
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager)
|
||||
{
|
||||
if (!pipelineManager)
|
||||
pipelineManager = std::unique_ptr<PipelineManager>(new PipelineManager());
|
||||
Drawer::Init(samplerManager, shaderManager);
|
||||
|
||||
if (descriptorSets.size() > GetContext()->GetSwapChainSize())
|
||||
descriptorSets.resize(GetContext()->GetSwapChainSize());
|
||||
else
|
||||
while (descriptorSets.size() < GetContext()->GetSwapChainSize())
|
||||
{
|
||||
descriptorSets.push_back(DescriptorSets());
|
||||
|
@ -95,12 +99,12 @@ protected:
|
|||
std::max(512 * 1024u, size),
|
||||
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eUniformBuffer)));
|
||||
}
|
||||
else if (mainBuffers[GetCurrentImage()]->m_size < size)
|
||||
else if (mainBuffers[GetCurrentImage()]->bufferSize < size)
|
||||
{
|
||||
u32 newSize = mainBuffers[GetCurrentImage()]->m_size;
|
||||
u32 newSize = mainBuffers[GetCurrentImage()]->bufferSize;
|
||||
while (newSize < size)
|
||||
newSize *= 2;
|
||||
INFO_LOG(RENDERER, "Increasing main buffer size %d -> %d", (u32)mainBuffers[GetCurrentImage()]->m_size, newSize);
|
||||
INFO_LOG(RENDERER, "Increasing main buffer size %d -> %d", (u32)mainBuffers[GetCurrentImage()]->bufferSize, newSize);
|
||||
mainBuffers[GetCurrentImage()] = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice().get(), newSize,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eUniformBuffer));
|
||||
}
|
||||
|
@ -131,13 +135,14 @@ private:
|
|||
class TextureDrawer : public Drawer
|
||||
{
|
||||
public:
|
||||
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager) override
|
||||
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager, VulkanAllocator *texAllocator)
|
||||
{
|
||||
pipelineManager = std::unique_ptr<RttPipelineManager>(new RttPipelineManager());
|
||||
Drawer::Init(samplerManager, shaderManager);
|
||||
|
||||
descriptorSets.Init(samplerManager, pipelineManager->GetPipelineLayout(), pipelineManager->GetPerFrameDSLayout(), pipelineManager->GetPerPolyDSLayout());
|
||||
fence = GetContext()->GetDevice()->createFenceUnique(vk::FenceCreateInfo());
|
||||
this->texAllocator = texAllocator;
|
||||
}
|
||||
void SetCommandPool(CommandPool *commandPool) { this->commandPool = commandPool; }
|
||||
|
||||
|
@ -148,12 +153,12 @@ protected:
|
|||
|
||||
virtual BufferData* GetMainBuffer(u32 size) override
|
||||
{
|
||||
if (!mainBuffer || mainBuffer->m_size < size)
|
||||
if (!mainBuffer || mainBuffer->bufferSize < size)
|
||||
{
|
||||
u32 newSize = mainBuffer ? mainBuffer->m_size : 128 * 1024u;
|
||||
u32 newSize = mainBuffer ? mainBuffer->bufferSize : 128 * 1024u;
|
||||
while (newSize < size)
|
||||
newSize *= 2;
|
||||
INFO_LOG(RENDERER, "Increasing RTT main buffer size %d -> %d", !mainBuffer ? 0 : (u32)mainBuffer->m_size, newSize);
|
||||
INFO_LOG(RENDERER, "Increasing RTT main buffer size %d -> %d", !mainBuffer ? 0 : (u32)mainBuffer->bufferSize, newSize);
|
||||
mainBuffer = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), *GetContext()->GetDevice(), newSize,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eUniformBuffer));
|
||||
}
|
||||
|
@ -176,4 +181,5 @@ private:
|
|||
DescriptorSets descriptorSets;
|
||||
std::unique_ptr<BufferData> mainBuffer;
|
||||
CommandPool *commandPool;
|
||||
VulkanAllocator *texAllocator;
|
||||
};
|
||||
|
|
|
@ -130,6 +130,8 @@ public:
|
|||
{
|
||||
this->shaderManager = shaderManager;
|
||||
|
||||
if (!perFrameLayout)
|
||||
{
|
||||
// Descriptor set and pipeline layout
|
||||
vk::DescriptorSetLayoutBinding perFrameBindings[] = {
|
||||
{ 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex }, // vertex uniforms
|
||||
|
@ -147,6 +149,7 @@ public:
|
|||
vk::PushConstantRange pushConstant(vk::ShaderStageFlagBits::eFragment, 0, 20);
|
||||
pipelineLayout = GetContext()->GetDevice()->createPipelineLayoutUnique(
|
||||
vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), ARRAY_SIZE(layouts), layouts, 1, &pushConstant));
|
||||
}
|
||||
|
||||
renderPass = VulkanContext::Instance()->GetRenderPass();
|
||||
}
|
||||
|
|
|
@ -169,7 +169,6 @@ void Texture::Init(u32 width, u32 height, vk::Format format)
|
|||
{
|
||||
this->extent = vk::Extent2D(width, height);
|
||||
this->format = format;
|
||||
vk::PhysicalDeviceMemoryProperties memoryProperties = physicalDevice.getMemoryProperties();
|
||||
vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format);
|
||||
|
||||
vk::FormatFeatureFlags formatFeatureFlags = vk::FormatFeatureFlagBits::eSampledImage;
|
||||
|
@ -182,10 +181,14 @@ void Texture::Init(u32 width, u32 height, vk::Format format)
|
|||
if (needsStaging)
|
||||
{
|
||||
verify((formatProperties.optimalTilingFeatures & formatFeatureFlags) == formatFeatureFlags);
|
||||
if (allocator)
|
||||
stagingBufferData = std::unique_ptr<BufferData>(new BufferData(physicalDevice, device, extent.width * extent.height * 4, vk::BufferUsageFlagBits::eTransferSrc, allocator));
|
||||
else
|
||||
stagingBufferData = std::unique_ptr<BufferData>(new BufferData(physicalDevice, device, extent.width * extent.height * 4, vk::BufferUsageFlagBits::eTransferSrc));
|
||||
imageTiling = vk::ImageTiling::eOptimal;
|
||||
usageFlags |= vk::ImageUsageFlagBits::eTransferDst;
|
||||
initialLayout = vk::ImageLayout::eUndefined;
|
||||
requirements = vk::MemoryPropertyFlagBits::eDeviceLocal;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -193,8 +196,7 @@ void Texture::Init(u32 width, u32 height, vk::Format format)
|
|||
initialLayout = vk::ImageLayout::ePreinitialized;
|
||||
requirements = vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible;
|
||||
}
|
||||
CreateImage(imageTiling, usageFlags, initialLayout, requirements,
|
||||
vk::ImageAspectFlagBits::eColor);
|
||||
CreateImage(imageTiling, usageFlags, initialLayout, requirements, vk::ImageAspectFlagBits::eColor);
|
||||
}
|
||||
|
||||
void Texture::CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout,
|
||||
|
@ -206,10 +208,19 @@ void Texture::CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk:
|
|||
image = device.createImageUnique(imageCreateInfo);
|
||||
|
||||
vk::MemoryRequirements memReq = device.getImageMemoryRequirements(image.get());
|
||||
u32 memoryTypeIndex = findMemoryType(physicalDevice.getMemoryProperties(), memReq.memoryTypeBits, memoryProperties);
|
||||
deviceMemory = device.allocateMemoryUnique(vk::MemoryAllocateInfo(memReq.size, memoryTypeIndex));
|
||||
memoryType = findMemoryType(physicalDevice.getMemoryProperties(), memReq.memoryTypeBits, memoryProperties);
|
||||
if (allocator)
|
||||
{
|
||||
if (sharedDeviceMemory)
|
||||
allocator->Free(memoryOffset, memoryType, sharedDeviceMemory);
|
||||
memoryOffset = allocator->Allocate(memReq.size, memReq.alignment, memoryType, sharedDeviceMemory);
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceMemory = device.allocateMemoryUnique(vk::MemoryAllocateInfo(memReq.size, memoryType));
|
||||
}
|
||||
|
||||
device.bindImageMemory(image.get(), deviceMemory.get(), 0);
|
||||
device.bindImageMemory(image.get(), allocator ? sharedDeviceMemory : *deviceMemory, memoryOffset);
|
||||
|
||||
vk::ImageViewCreateInfo imageViewCreateInfo(vk::ImageViewCreateFlags(), image.get(), vk::ImageViewType::e2D, format, vk::ComponentMapping(),
|
||||
vk::ImageSubresourceRange(aspectMask, 0, 1, 0, 1));
|
||||
|
@ -227,13 +238,13 @@ void Texture::SetImage(u32 srcSize, void *srcData, bool isNew)
|
|||
? device.getBufferMemoryRequirements(stagingBufferData->buffer.get()).size
|
||||
: device.getImageMemoryRequirements(image.get()).size;
|
||||
void* data = needsStaging
|
||||
? device.mapMemory(stagingBufferData->deviceMemory.get(), 0, size)
|
||||
: device.mapMemory(deviceMemory.get(), 0, size);
|
||||
? stagingBufferData->MapMemory()
|
||||
: device.mapMemory(allocator ? sharedDeviceMemory : *deviceMemory, memoryOffset, size);
|
||||
memcpy(data, srcData, srcSize);
|
||||
device.unmapMemory(needsStaging ? stagingBufferData->deviceMemory.get() : deviceMemory.get());
|
||||
|
||||
if (needsStaging)
|
||||
{
|
||||
stagingBufferData->UnmapMemory();
|
||||
// Since we're going to blit to the texture image, set its layout to eTransferDstOptimal
|
||||
setImageLayout(commandBuffer, image.get(), format, isNew ? vk::ImageLayout::eUndefined : vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
vk::ImageLayout::eTransferDstOptimal);
|
||||
|
@ -244,6 +255,7 @@ void Texture::SetImage(u32 srcSize, void *srcData, bool isNew)
|
|||
}
|
||||
else
|
||||
{
|
||||
device.unmapMemory(allocator ? sharedDeviceMemory : *deviceMemory);
|
||||
// If we can use the linear tiled image as a texture, just do it
|
||||
setImageLayout(commandBuffer, image.get(), format, vk::ImageLayout::ePreinitialized, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
}
|
||||
|
|
|
@ -29,9 +29,16 @@ void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image image, vk:
|
|||
|
||||
struct Texture : BaseTextureCacheData
|
||||
{
|
||||
Texture(vk::PhysicalDevice physicalDevice, vk::Device device)
|
||||
: physicalDevice(physicalDevice), device(device), format(vk::Format::eUndefined)
|
||||
Texture(vk::PhysicalDevice physicalDevice, vk::Device device, VulkanAllocator *allocator = nullptr)
|
||||
: physicalDevice(physicalDevice), device(device), format(vk::Format::eUndefined), allocator(allocator)
|
||||
{}
|
||||
~Texture() override
|
||||
{
|
||||
imageView.reset();
|
||||
image.reset();
|
||||
if (allocator)
|
||||
allocator->Free(memoryOffset, memoryType, sharedDeviceMemory);
|
||||
}
|
||||
void UploadToGPU(int width, int height, u8 *data) override;
|
||||
u64 GetIntId() { return (u64)reinterpret_cast<uintptr_t>(this); }
|
||||
std::string GetId() override { char s[20]; sprintf(s, "%p", this); return s; }
|
||||
|
@ -57,6 +64,10 @@ private:
|
|||
|
||||
vk::PhysicalDevice physicalDevice;
|
||||
vk::Device device;
|
||||
VulkanAllocator *allocator;
|
||||
vk::DeviceMemory sharedDeviceMemory;
|
||||
u32 memoryType = 0;
|
||||
vk::DeviceSize memoryOffset = 0;
|
||||
|
||||
friend class TextureDrawer;
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "vulkan.h"
|
||||
|
||||
static inline u32 findMemoryType(vk::PhysicalDeviceMemoryProperties const& memoryProperties, u32 typeBits, vk::MemoryPropertyFlags requirementsMask)
|
||||
|
|
|
@ -371,8 +371,8 @@ void VulkanContext::CreateSwapChain()
|
|||
if (surfaceCapabilities.currentExtent.width == std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
// If the surface size is undefined, the size is set to the size of the images requested.
|
||||
swapchainExtent.width = std::min(std::max(width, surfaceCapabilities.minImageExtent.width), surfaceCapabilities.maxImageExtent.width);
|
||||
swapchainExtent.height = std::min(std::max(height, surfaceCapabilities.minImageExtent.height), surfaceCapabilities.maxImageExtent.height);
|
||||
swapchainExtent.width = std::min(std::max(640u, surfaceCapabilities.minImageExtent.width), surfaceCapabilities.maxImageExtent.width);
|
||||
swapchainExtent.height = std::min(std::max(480u, surfaceCapabilities.minImageExtent.height), surfaceCapabilities.maxImageExtent.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <memory>
|
||||
#include "vulkan.h"
|
||||
#include "hw/pvr/Renderer_if.h"
|
||||
#include "allocator.h"
|
||||
#include "commandpool.h"
|
||||
#include "drawer.h"
|
||||
#include "shaders.h"
|
||||
|
@ -36,7 +37,8 @@ public:
|
|||
shaderManager.Init();
|
||||
texCommandPool.Init();
|
||||
|
||||
textureDrawer.Init(&samplerManager, &shaderManager);
|
||||
texAllocator.SetChunkSize(16 * 1024 * 1024);
|
||||
textureDrawer.Init(&samplerManager, &shaderManager, &texAllocator);
|
||||
textureDrawer.SetCommandPool(&texCommandPool);
|
||||
screenDrawer.Init(&samplerManager, &shaderManager);
|
||||
|
||||
|
@ -45,6 +47,8 @@ public:
|
|||
|
||||
void Resize(int w, int h) override
|
||||
{
|
||||
texCommandPool.Init();
|
||||
screenDrawer.Init(&samplerManager, &shaderManager);
|
||||
}
|
||||
|
||||
void Term() override
|
||||
|
@ -52,15 +56,21 @@ public:
|
|||
printf("VulkanRenderer::Term\n");
|
||||
GetContext()->WaitIdle();
|
||||
killtex();
|
||||
fogTexture = nullptr;
|
||||
texCommandPool.Term();
|
||||
shaderManager.Term();
|
||||
}
|
||||
|
||||
void RenderFramebuffer()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
bool Process(TA_context* ctx) override
|
||||
{
|
||||
if (ctx->rend.isRenderFramebuffer)
|
||||
{
|
||||
// TODO RenderFramebuffer();
|
||||
RenderFramebuffer();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -96,8 +106,8 @@ public:
|
|||
|
||||
virtual u64 GetTexture(TSP tsp, TCW tcw) override
|
||||
{
|
||||
Texture* tf = static_cast<Texture*>(getTextureCacheData(tsp, tcw, [](){
|
||||
return (BaseTextureCacheData *)new Texture(VulkanContext::Instance()->GetPhysicalDevice(), *VulkanContext::Instance()->GetDevice());
|
||||
Texture* tf = static_cast<Texture*>(getTextureCacheData(tsp, tcw, [this](){
|
||||
return (BaseTextureCacheData *)new Texture(VulkanContext::Instance()->GetPhysicalDevice(), *VulkanContext::Instance()->GetDevice(), &this->texAllocator);
|
||||
}));
|
||||
|
||||
if (tf->IsNew())
|
||||
|
@ -146,6 +156,7 @@ private:
|
|||
ShaderManager shaderManager;
|
||||
ScreenDrawer screenDrawer;
|
||||
TextureDrawer textureDrawer;
|
||||
VulkanAllocator texAllocator;
|
||||
};
|
||||
|
||||
Renderer* rend_Vulkan()
|
||||
|
|
Loading…
Reference in New Issue