Merge pull request #39 from georgemoralis/video_buffers
work on buffers graphics somehow working
This commit is contained in:
commit
56a7398fa6
|
@ -33,3 +33,11 @@
|
||||||
path = third-party/toml11
|
path = third-party/toml11
|
||||||
url = https://github.com/ToruNiina/toml11
|
url = https://github.com/ToruNiina/toml11
|
||||||
branch = master
|
branch = master
|
||||||
|
[submodule "third-party/vulkan"]
|
||||||
|
path = third-party/vulkan
|
||||||
|
url = https://github.com/shadps4/vulkan.git
|
||||||
|
branch = main
|
||||||
|
[submodule "third-party/xxHash"]
|
||||||
|
path = third-party/xxHash
|
||||||
|
url = https://github.com/Cyan4973/xxHash.git
|
||||||
|
branch = dev
|
||||||
|
|
|
@ -17,6 +17,8 @@ include_directories(third-party/fmt/include)
|
||||||
include_directories(third-party/magic_enum/include)
|
include_directories(third-party/magic_enum/include)
|
||||||
include_directories(third-party/zydis/include/Zydis)
|
include_directories(third-party/zydis/include/Zydis)
|
||||||
include_directories(third-party/winpthread/include)
|
include_directories(third-party/winpthread/include)
|
||||||
|
include_directories(third-party/vulkan/include)
|
||||||
|
include_directories(third-party/xxhash/include)
|
||||||
add_subdirectory("third-party")
|
add_subdirectory("third-party")
|
||||||
#=================== EXAMPLE ===================
|
#=================== EXAMPLE ===================
|
||||||
include_directories(src)
|
include_directories(src)
|
||||||
|
@ -52,10 +54,10 @@ add_executable(shadps4
|
||||||
src/Core/PS4/HLE/Kernel/event_queues.h
|
src/Core/PS4/HLE/Kernel/event_queues.h
|
||||||
src/Core/PS4/HLE/Kernel/cpu_management.cpp
|
src/Core/PS4/HLE/Kernel/cpu_management.cpp
|
||||||
src/Core/PS4/HLE/Kernel/cpu_management.h
|
src/Core/PS4/HLE/Kernel/cpu_management.h
|
||||||
"src/Util/Singleton.h" "src/Util/Disassembler.cpp" "src/Util/Disassembler.h" "src/Core/PS4/Util/aerolib.h" "src/Core/PS4/Loader/SymbolsResolver.h" "src/Core/PS4/Loader/SymbolsResolver.cpp" "src/Core/PS4/HLE/Libs.cpp" "src/Core/PS4/HLE/Libs.h" "src/Core/PS4/HLE/LibC.cpp" "src/Core/PS4/HLE/LibC.h" "src/Lib/Timer.cpp" "src/Lib/Timer.h" "src/Core/PS4/HLE/LibKernel.cpp" "src/Core/PS4/HLE/LibKernel.h" "src/Core/PS4/HLE/LibSceGnmDriver.cpp" "src/Core/PS4/HLE/LibSceGnmDriver.h" "src/Core/PS4/HLE/Kernel/ThreadManagement.cpp" "src/Core/PS4/HLE/Kernel/ThreadManagement.h" "src/Core/PS4/HLE/ErrorCodes.h" "src/debug.h" "src/Core/PS4/HLE/Kernel/memory_management.cpp" "src/Core/PS4/HLE/Kernel/memory_management.h" "src/Core/PS4/GPU/gpu_memory.cpp" "src/Core/PS4/GPU/gpu_memory.h" "src/emulator.cpp" "src/emulator.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.h" "src/Core/PS4/HLE/Graphics/graphics_ctx.h")
|
"src/Util/Singleton.h" "src/Util/Disassembler.cpp" "src/Util/Disassembler.h" "src/Core/PS4/Util/aerolib.h" "src/Core/PS4/Loader/SymbolsResolver.h" "src/Core/PS4/Loader/SymbolsResolver.cpp" "src/Core/PS4/HLE/Libs.cpp" "src/Core/PS4/HLE/Libs.h" "src/Core/PS4/HLE/LibC.cpp" "src/Core/PS4/HLE/LibC.h" "src/Lib/Timer.cpp" "src/Lib/Timer.h" "src/Core/PS4/HLE/LibKernel.cpp" "src/Core/PS4/HLE/LibKernel.h" "src/Core/PS4/HLE/LibSceGnmDriver.cpp" "src/Core/PS4/HLE/LibSceGnmDriver.h" "src/Core/PS4/HLE/Kernel/ThreadManagement.cpp" "src/Core/PS4/HLE/Kernel/ThreadManagement.h" "src/Core/PS4/HLE/ErrorCodes.h" "src/debug.h" "src/Core/PS4/HLE/Kernel/memory_management.cpp" "src/Core/PS4/HLE/Kernel/memory_management.h" "src/Core/PS4/GPU/gpu_memory.cpp" "src/Core/PS4/GPU/gpu_memory.h" "src/emulator.cpp" "src/emulator.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.h" "src/Core/PS4/HLE/Graphics/graphics_ctx.h" "src/vulkan_util.cpp" "src/vulkan_util.h" "src/Core/PS4/GPU/video_out_buffer.cpp" "src/Core/PS4/GPU/video_out_buffer.h" "src/Core/PS4/HLE/Graphics/graphics_render.cpp" "src/Core/PS4/HLE/Graphics/graphics_render.h")
|
||||||
|
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
target_link_libraries(shadps4 PUBLIC fmt mincore spdlog IMGUI SDL3-shared ${OPENGL_LIBRARY})
|
target_link_libraries(shadps4 PUBLIC fmt mincore spdlog IMGUI SDL3-shared ${OPENGL_LIBRARY} vulkan-1 spirv-tools-opt spirv-tools)
|
||||||
|
|
||||||
add_custom_command(TARGET shadps4 POST_BUILD
|
add_custom_command(TARGET shadps4 POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
|
|
@ -1,5 +1,188 @@
|
||||||
#include "gpu_memory.h"
|
#include "gpu_memory.h"
|
||||||
|
|
||||||
namespace GPU {
|
#include <xxhash/xxh3.h>
|
||||||
void MemorySetAllocArea(u64 virtual_addr, u64 size) {}
|
|
||||||
} // namespace GPU
|
#include "Util/Singleton.h"
|
||||||
|
|
||||||
|
void* GPU::memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, void* todo /*CommandBuffer?*/, u64 virtual_addr, u64 size,
|
||||||
|
const GPUObject& info) {
|
||||||
|
auto* gpumemory = Singleton<GPUMemory>::Instance();
|
||||||
|
|
||||||
|
return gpumemory->memoryCreateObj(submit_id, ctx, nullptr, &virtual_addr, &size, 1, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::memorySetAllocArea(u64 virtual_addr, u64 size) {
|
||||||
|
auto* gpumemory = Singleton<GPUMemory>::Instance();
|
||||||
|
|
||||||
|
Lib::LockMutexGuard lock(gpumemory->m_mutex);
|
||||||
|
|
||||||
|
MemoryHeap h;
|
||||||
|
h.allocated_virtual_addr = virtual_addr;
|
||||||
|
h.allocated_size = size;
|
||||||
|
|
||||||
|
gpumemory->m_heaps.push_back(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GPU::calculate_hash(const u08* buf, u64 size) { return (size > 0 && buf != nullptr ? XXH3_64bits(buf, size) : 0); }
|
||||||
|
|
||||||
|
bool GPU::vulkanAllocateMemory(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanMemory* mem) {
|
||||||
|
static std::atomic_uint64_t unique_id = 0;
|
||||||
|
|
||||||
|
VkPhysicalDeviceMemoryProperties memory_properties{};
|
||||||
|
vkGetPhysicalDeviceMemoryProperties(ctx->m_physical_device, &memory_properties);
|
||||||
|
|
||||||
|
u32 index = 0;
|
||||||
|
for (; index < memory_properties.memoryTypeCount; index++) {
|
||||||
|
if ((mem->requirements.memoryTypeBits & (static_cast<uint32_t>(1) << index)) != 0 &&
|
||||||
|
(memory_properties.memoryTypes[index].propertyFlags & mem->property) == mem->property) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mem->type = index;
|
||||||
|
mem->offset = 0;
|
||||||
|
|
||||||
|
VkMemoryAllocateInfo alloc_info{};
|
||||||
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
|
alloc_info.pNext = nullptr;
|
||||||
|
alloc_info.allocationSize = mem->requirements.size;
|
||||||
|
alloc_info.memoryTypeIndex = index;
|
||||||
|
|
||||||
|
mem->unique_id = ++unique_id;
|
||||||
|
|
||||||
|
auto result = vkAllocateMemory(ctx->m_device, &alloc_info, nullptr, &mem->memory);
|
||||||
|
|
||||||
|
if (result == VK_SUCCESS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::flushGarlic(HLE::Libs::Graphics::GraphicCtx* ctx) {
|
||||||
|
auto* gpumemory = Singleton<GPUMemory>::Instance();
|
||||||
|
gpumemory->flushAllHeaps(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GPU::GPUMemory::getHeapId(u64 virtual_addr, u64 size) {
|
||||||
|
int index = 0;
|
||||||
|
for (const auto& heap : m_heaps) {
|
||||||
|
if ((virtual_addr >= heap.allocated_virtual_addr && virtual_addr < heap.allocated_virtual_addr + heap.allocated_size) ||
|
||||||
|
((virtual_addr + size - 1) >= heap.allocated_virtual_addr &&
|
||||||
|
(virtual_addr + size - 1) < heap.allocated_virtual_addr + heap.allocated_size)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GPU::GPUMemory::memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, void* todo, const u64* virtual_addr, const u64* size,
|
||||||
|
int virtual_addr_num, const GPUObject& info) {
|
||||||
|
auto* gpumemory = Singleton<GPUMemory>::Instance();
|
||||||
|
|
||||||
|
Lib::LockMutexGuard lock(gpumemory->m_mutex);
|
||||||
|
|
||||||
|
int heap_id = gpumemory->getHeapId(virtual_addr[0], size[0]);
|
||||||
|
|
||||||
|
if (heap_id < 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto& heap = m_heaps[heap_id];
|
||||||
|
|
||||||
|
ObjInfo objInfo = {};
|
||||||
|
|
||||||
|
// copy parameters from info to obj
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
objInfo.obj_params[i] = info.obj_params[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
objInfo.gpu_object.objectType = info.objectType;
|
||||||
|
objInfo.gpu_object.obj = nullptr;
|
||||||
|
|
||||||
|
for (int h = 0; h < virtual_addr_num; h++) {
|
||||||
|
if (info.check_hash) {
|
||||||
|
objInfo.hash[h] = GPU::calculate_hash(reinterpret_cast<const u08*>(virtual_addr[h]), size[h]);
|
||||||
|
} else {
|
||||||
|
objInfo.hash[h] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objInfo.submit_id = submit_id;
|
||||||
|
objInfo.check_hash = info.check_hash;
|
||||||
|
|
||||||
|
objInfo.gpu_object.obj = info.getCreateFunc()(ctx, objInfo.obj_params, virtual_addr, size, virtual_addr_num, &objInfo.mem);
|
||||||
|
|
||||||
|
objInfo.update_func = info.getUpdateFunc();
|
||||||
|
int index = static_cast<int>(heap.objects.size());
|
||||||
|
|
||||||
|
HeapObject hobj{};
|
||||||
|
hobj.block = createHeapBlock(virtual_addr, size, virtual_addr_num, heap_id, index);
|
||||||
|
hobj.info = objInfo;
|
||||||
|
hobj.free = false;
|
||||||
|
heap.objects.push_back(hobj);
|
||||||
|
|
||||||
|
return objInfo.gpu_object.obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPU::HeapBlock GPU::GPUMemory::createHeapBlock(const u64* virtual_addr, const u64* size, int virtual_addr_num, int heap_id, int obj_id) {
|
||||||
|
auto& heap = m_heaps[heap_id];
|
||||||
|
|
||||||
|
GPU::HeapBlock heapBlock{};
|
||||||
|
heapBlock.virtual_addr_num = virtual_addr_num;
|
||||||
|
for (int vi = 0; vi < virtual_addr_num; vi++) {
|
||||||
|
heapBlock.virtual_addr[vi] = virtual_addr[vi];
|
||||||
|
heapBlock.size[vi] = size[vi];
|
||||||
|
}
|
||||||
|
return heapBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::GPUMemory::update(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, int heap_id, int obj_id) {
|
||||||
|
auto& heap = m_heaps[heap_id];
|
||||||
|
|
||||||
|
auto& heapObj = heap.objects[obj_id];
|
||||||
|
auto& objInfo = heapObj.info;
|
||||||
|
bool need_update = false;
|
||||||
|
|
||||||
|
if (submit_id > objInfo.submit_id) {
|
||||||
|
uint64_t hash[3] = {};
|
||||||
|
|
||||||
|
for (int i = 0; i < heapObj.block.virtual_addr_num; i++) {
|
||||||
|
if (objInfo.check_hash) {
|
||||||
|
hash[i] = GPU::calculate_hash(reinterpret_cast<const uint8_t*>(heapObj.block.virtual_addr[i]), heapObj.block.size[i]);
|
||||||
|
} else {
|
||||||
|
hash[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < heapObj.block.virtual_addr_num; i++) {
|
||||||
|
if (objInfo.hash[i] != hash[i]) {
|
||||||
|
need_update = true;
|
||||||
|
objInfo.hash[i] = hash[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submit_id != UINT64_MAX) {
|
||||||
|
objInfo.submit_id = submit_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_update) {
|
||||||
|
objInfo.update_func(ctx, objInfo.obj_params, objInfo.gpu_object.obj, heapObj.block.virtual_addr, heapObj.block.size,
|
||||||
|
heapObj.block.virtual_addr_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::GPUMemory::flushAllHeaps(HLE::Libs::Graphics::GraphicCtx* ctx) {
|
||||||
|
Lib::LockMutexGuard lock(m_mutex);
|
||||||
|
|
||||||
|
int heap_id = 0;
|
||||||
|
for (auto& heap : m_heaps) {
|
||||||
|
int index = 0;
|
||||||
|
for (auto& heapObj : heap.objects) {
|
||||||
|
if (!heapObj.free) {
|
||||||
|
update(UINT64_MAX, ctx, heap_id, index);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
heap_id++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,84 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace GPU {
|
namespace GPU {
|
||||||
enum class MemoryMode : u32 { NoAccess = 0, Read = 1, Write = 2, ReadWrite = 3 };
|
|
||||||
|
|
||||||
void MemorySetAllocArea(u64 virtual_addr, u64 size);
|
class GPUObject;
|
||||||
}
|
|
||||||
|
enum class MemoryMode : u32 { NoAccess = 0, Read = 1, Write = 2, ReadWrite = 3 };
|
||||||
|
enum class MemoryObjectType : u64 { InvalidObj, VideoOutBufferObj };
|
||||||
|
|
||||||
|
struct GpuMemoryObject {
|
||||||
|
MemoryObjectType objectType = MemoryObjectType::InvalidObj;
|
||||||
|
void* obj = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HeapBlock {
|
||||||
|
u64 virtual_addr[3] = {};
|
||||||
|
u64 size[3] = {};
|
||||||
|
int virtual_addr_num = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GPUObject {
|
||||||
|
public:
|
||||||
|
GPUObject() = default;
|
||||||
|
virtual ~GPUObject() = default;
|
||||||
|
u64 obj_params[8] = {};
|
||||||
|
bool check_hash = false;
|
||||||
|
bool isReadOnly = false;
|
||||||
|
MemoryObjectType objectType = MemoryObjectType::InvalidObj;
|
||||||
|
|
||||||
|
using create_func_t = void* (*)(HLE::Libs::Graphics::GraphicCtx* ctx, const u64* params, const u64* virtual_addr, const u64* size, int virtual_addr_num,
|
||||||
|
HLE::Libs::Graphics::VulkanMemory* mem);
|
||||||
|
using update_func_t = void (*)(HLE::Libs::Graphics::GraphicCtx* ctx, const u64* params, void* obj, const u64* virtual_addr, const u64* size,
|
||||||
|
int virtual_addr_num);
|
||||||
|
|
||||||
|
virtual create_func_t getCreateFunc() const = 0;
|
||||||
|
virtual update_func_t getUpdateFunc() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjInfo {
|
||||||
|
u64 obj_params[8] = {};
|
||||||
|
GpuMemoryObject gpu_object;
|
||||||
|
u64 hash[3] = {};
|
||||||
|
u64 submit_id = 0;
|
||||||
|
bool check_hash = false;
|
||||||
|
HLE::Libs::Graphics::VulkanMemory mem;
|
||||||
|
GPU::GPUObject::update_func_t update_func = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HeapObject {
|
||||||
|
HeapBlock block;
|
||||||
|
ObjInfo info;
|
||||||
|
bool free = true;
|
||||||
|
};
|
||||||
|
struct MemoryHeap {
|
||||||
|
u64 allocated_virtual_addr = 0;
|
||||||
|
u64 allocated_size = 0;
|
||||||
|
std::vector<HeapObject> objects;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GPUMemory {
|
||||||
|
public:
|
||||||
|
GPUMemory() {}
|
||||||
|
virtual ~GPUMemory() {}
|
||||||
|
int getHeapId(u64 vaddr, u64 size);
|
||||||
|
Lib::Mutex m_mutex;
|
||||||
|
std::vector<MemoryHeap> m_heaps;
|
||||||
|
void* memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, /*CommandBuffer* buffer*/ void* todo, const u64* virtual_addr,
|
||||||
|
const u64* size, int virtual_addr_num, const GPUObject& info);
|
||||||
|
HeapBlock createHeapBlock(const u64* virtual_addr, const u64* size, int virtual_addr_num, int heap_id, int obj_id);
|
||||||
|
void update(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, int heap_id, int obj_id);
|
||||||
|
void flushAllHeaps(HLE::Libs::Graphics::GraphicCtx* ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
void memorySetAllocArea(u64 virtual_addr, u64 size);
|
||||||
|
void* memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, /*CommandBuffer* buffer*/ void* todo, u64 virtual_addr, u64 size,
|
||||||
|
const GPUObject& info);
|
||||||
|
u64 calculate_hash(const u08* buf, u64 size);
|
||||||
|
bool vulkanAllocateMemory(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanMemory* mem);
|
||||||
|
void flushGarlic(HLE::Libs::Graphics::GraphicCtx* ctx);
|
||||||
|
} // namespace GPU
|
|
@ -0,0 +1,126 @@
|
||||||
|
#include "video_out_buffer.h"
|
||||||
|
|
||||||
|
#include <Util/log.h>
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
#include <vulkan_util.h>
|
||||||
|
|
||||||
|
constexpr bool log_file_videoOutBuffer = true; // disable it to disable logging
|
||||||
|
|
||||||
|
static void update_func(HLE::Libs::Graphics::GraphicCtx* ctx, const u64* params, void* obj, const u64* virtual_addr, const u64* size,
|
||||||
|
int virtual_addr_num) {
|
||||||
|
|
||||||
|
auto pitch = params[GPU::VideoOutBufferObj::PITCH_PARAM];
|
||||||
|
|
||||||
|
auto* vk_obj = static_cast<HLE::Libs::Graphics::VideoOutVulkanImage*>(obj);
|
||||||
|
|
||||||
|
vk_obj->layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
|
||||||
|
Graphics::Vulkan::vulkanFillImage(ctx, vk_obj, reinterpret_cast<void*>(*virtual_addr), *size, pitch,
|
||||||
|
static_cast<uint64_t>(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* create_func(HLE::Libs::Graphics::GraphicCtx* ctx, const u64* params, const u64* virtual_addr, const u64* size, int virtual_addr_num,
|
||||||
|
HLE::Libs::Graphics::VulkanMemory* mem) {
|
||||||
|
auto pixel_format = params[GPU::VideoOutBufferObj::PIXEL_FORMAT_PARAM];
|
||||||
|
auto width = params[GPU::VideoOutBufferObj::WIDTH_PARAM];
|
||||||
|
auto height = params[GPU::VideoOutBufferObj::HEIGHT_PARAM];
|
||||||
|
|
||||||
|
auto* vk_obj = new HLE::Libs::Graphics::VideoOutVulkanImage;
|
||||||
|
|
||||||
|
VkFormat vk_format = VK_FORMAT_UNDEFINED;
|
||||||
|
|
||||||
|
switch (pixel_format) {
|
||||||
|
case static_cast<uint64_t>(GPU::VideoOutBufferFormat::R8G8B8A8Srgb): vk_format = VK_FORMAT_R8G8B8A8_SRGB; break;
|
||||||
|
case static_cast<uint64_t>(GPU::VideoOutBufferFormat::B8G8R8A8Srgb): vk_format = VK_FORMAT_B8G8R8A8_SRGB; break;
|
||||||
|
default: LOG_CRITICAL_IF(log_file_videoOutBuffer, "unknown pixelFormat = {}\n", pixel_format); std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk_obj->extent.width = width;
|
||||||
|
vk_obj->extent.height = height;
|
||||||
|
vk_obj->format = vk_format;
|
||||||
|
vk_obj->image = nullptr;
|
||||||
|
vk_obj->layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
|
||||||
|
for (auto& view : vk_obj->image_view) {
|
||||||
|
view = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageCreateInfo image_info{};
|
||||||
|
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||||
|
image_info.pNext = nullptr;
|
||||||
|
image_info.flags = 0;
|
||||||
|
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
image_info.extent.width = vk_obj->extent.width;
|
||||||
|
image_info.extent.height = vk_obj->extent.height;
|
||||||
|
image_info.extent.depth = 1;
|
||||||
|
image_info.mipLevels = 1;
|
||||||
|
image_info.arrayLayers = 1;
|
||||||
|
image_info.format = vk_obj->format;
|
||||||
|
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
|
image_info.initialLayout = vk_obj->layout;
|
||||||
|
image_info.usage =
|
||||||
|
static_cast<VkImageUsageFlags>(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT) | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
|
||||||
|
vkCreateImage(ctx->m_device, &image_info, nullptr, &vk_obj->image);
|
||||||
|
|
||||||
|
if (vk_obj->image == nullptr) {
|
||||||
|
LOG_CRITICAL_IF(log_file_videoOutBuffer, "vk_obj->image is null\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkGetImageMemoryRequirements(ctx->m_device, vk_obj->image, &mem->requirements);
|
||||||
|
|
||||||
|
mem->property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
|
|
||||||
|
bool allocated = GPU::vulkanAllocateMemory(ctx, mem);
|
||||||
|
|
||||||
|
if (!allocated) {
|
||||||
|
LOG_CRITICAL_IF(log_file_videoOutBuffer, "can't allocate vulkan memory\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkBindImageMemory(ctx->m_device, vk_obj->image, mem->memory, mem->offset);
|
||||||
|
|
||||||
|
vk_obj->memory = *mem;
|
||||||
|
|
||||||
|
LOG_INFO_IF(log_file_videoOutBuffer, "videoOutBuffer create object\n");
|
||||||
|
LOG_INFO_IF(log_file_videoOutBuffer, "mem requirements.size = {}\n", mem->requirements.size);
|
||||||
|
LOG_INFO_IF(log_file_videoOutBuffer, "width = {}\n", width);
|
||||||
|
LOG_INFO_IF(log_file_videoOutBuffer, "height = {}\n", height);
|
||||||
|
LOG_INFO_IF(log_file_videoOutBuffer, "size = {}\n", *size);
|
||||||
|
|
||||||
|
update_func(ctx, params, vk_obj, virtual_addr, size, virtual_addr_num);
|
||||||
|
|
||||||
|
VkImageViewCreateInfo create_info{};
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
create_info.pNext = nullptr;
|
||||||
|
create_info.flags = 0;
|
||||||
|
create_info.image = vk_obj->image;
|
||||||
|
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
create_info.format = vk_obj->format;
|
||||||
|
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
create_info.subresourceRange.baseArrayLayer = 0;
|
||||||
|
create_info.subresourceRange.baseMipLevel = 0;
|
||||||
|
create_info.subresourceRange.layerCount = 1;
|
||||||
|
create_info.subresourceRange.levelCount = 1;
|
||||||
|
|
||||||
|
vkCreateImageView(ctx->m_device, &create_info, nullptr, &vk_obj->image_view[HLE::Libs::Graphics::VulkanImage::VIEW_DEFAULT]);
|
||||||
|
|
||||||
|
if (vk_obj->image_view[HLE::Libs::Graphics::VulkanImage::VIEW_DEFAULT] == nullptr) {
|
||||||
|
LOG_CRITICAL_IF(log_file_videoOutBuffer, "vk_obj->image_view is null\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vk_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPU::GPUObject::create_func_t GPU::VideoOutBufferObj::getCreateFunc() const { return create_func; }
|
||||||
|
|
||||||
|
GPU::GPUObject::update_func_t GPU::VideoOutBufferObj::getUpdateFunc() const { return update_func; }
|
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <types.h>
|
||||||
|
|
||||||
|
#include "gpu_memory.h"
|
||||||
|
|
||||||
|
namespace GPU {
|
||||||
|
|
||||||
|
enum class VideoOutBufferFormat : u64 {
|
||||||
|
Unknown,
|
||||||
|
R8G8B8A8Srgb,
|
||||||
|
B8G8R8A8Srgb,
|
||||||
|
};
|
||||||
|
|
||||||
|
class VideoOutBufferObj : public GPUObject {
|
||||||
|
public:
|
||||||
|
static constexpr int PIXEL_FORMAT_PARAM = 0;
|
||||||
|
static constexpr int WIDTH_PARAM = 1;
|
||||||
|
static constexpr int HEIGHT_PARAM = 2;
|
||||||
|
static constexpr int IS_TILE_PARAM = 3;
|
||||||
|
static constexpr int IS_NEO_PARAM = 4;
|
||||||
|
static constexpr int PITCH_PARAM = 5;
|
||||||
|
|
||||||
|
explicit VideoOutBufferObj(VideoOutBufferFormat pixel_format, u32 width, u32 height, bool is_tiled, bool is_neo, u32 pitch) {
|
||||||
|
obj_params[PIXEL_FORMAT_PARAM] = static_cast<uint64_t>(pixel_format);
|
||||||
|
obj_params[WIDTH_PARAM] = width;
|
||||||
|
obj_params[HEIGHT_PARAM] = height;
|
||||||
|
obj_params[IS_TILE_PARAM] = is_tiled ? 1 : 0;
|
||||||
|
obj_params[IS_NEO_PARAM] = is_neo ? 1 : 0;
|
||||||
|
obj_params[PITCH_PARAM] = pitch;
|
||||||
|
check_hash = true;
|
||||||
|
objectType = GPU::MemoryObjectType::VideoOutBufferObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_func_t getCreateFunc() const override;
|
||||||
|
update_func_t getUpdateFunc() const override;
|
||||||
|
};
|
||||||
|
} // namespace GPU
|
|
@ -9,9 +9,14 @@ constexpr int SCE_KERNEL_ERROR_EAGAIN = 0x80020023; // Memory cannot be a
|
||||||
constexpr int SCE_KERNEL_ERROR_ENAMETOOLONG = 0x8002003f; // character strings exceeds valid size
|
constexpr int SCE_KERNEL_ERROR_ENAMETOOLONG = 0x8002003f; // character strings exceeds valid size
|
||||||
|
|
||||||
// videoOut
|
// videoOut
|
||||||
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; // invalid argument
|
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; // invalid argument
|
||||||
constexpr int SCE_VIDEO_OUT_ERROR_RESOURCE_BUSY = 0x80290009; // already opened
|
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; // invalid addresses
|
||||||
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_INDEX = 0x8029000A; // invalid buffer index
|
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_TILING_MODE = 0x80290007; // invalid tiling mode
|
||||||
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_HANDLE = 0x8029000B; // invalid handle
|
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO = 0x80290008; // invalid aspect ration
|
||||||
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = 0x8029000C; // Invalid event queue
|
constexpr int SCE_VIDEO_OUT_ERROR_RESOURCE_BUSY = 0x80290009; // already opened
|
||||||
constexpr int SCE_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = 0x80290012; // flip queue is full
|
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_INDEX = 0x8029000A; // invalid buffer index
|
||||||
|
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_HANDLE = 0x8029000B; // invalid handle
|
||||||
|
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = 0x8029000C; // Invalid event queue
|
||||||
|
constexpr int SCE_VIDEO_OUT_ERROR_SLOT_OCCUPIED = 0x80290010; // slot already used
|
||||||
|
constexpr int SCE_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = 0x80290012; // flip queue is full
|
||||||
|
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_OPTION = 0x8029001A; // Invalid buffer attribute option
|
||||||
|
|
|
@ -76,6 +76,12 @@ bool FlipQueue::flip(u32 micros) {
|
||||||
auto request = m_requests.at(0); // proceed first request
|
auto request = m_requests.at(0); // proceed first request
|
||||||
m_mutex.UnlockMutex();
|
m_mutex.UnlockMutex();
|
||||||
|
|
||||||
|
auto* buffer = request.cfg->buffers[request.index].buffer_render;
|
||||||
|
|
||||||
|
Emulator::DrawBuffer(buffer);
|
||||||
|
|
||||||
|
m_mutex.LockMutex();
|
||||||
|
|
||||||
request.cfg->m_mutex.LockMutex();
|
request.cfg->m_mutex.LockMutex();
|
||||||
for (auto& flip_eq : request.cfg->m_flip_evtEq) {
|
for (auto& flip_eq : request.cfg->m_flip_evtEq) {
|
||||||
if (flip_eq != nullptr) {
|
if (flip_eq != nullptr) {
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <Core/PS4/HLE/Graphics/video_out.h>
|
#include <Core/PS4/HLE/Graphics/video_out.h>
|
||||||
#include <Lib/Threads.h>
|
#include <Lib/Threads.h>
|
||||||
|
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
|
||||||
|
#include <emulator.h>
|
||||||
|
|
||||||
using namespace HLE::Libs::Graphics::VideoOut;
|
using namespace HLE::Libs::Graphics::VideoOut;
|
||||||
|
|
||||||
namespace HLE::Graphics::Objects {
|
namespace HLE::Graphics::Objects {
|
||||||
|
|
||||||
//class FlipQueue;
|
struct VideoOutBufferInfo {
|
||||||
|
const void* buffer = nullptr;
|
||||||
|
HLE::Libs::Graphics::VideoOutVulkanImage* buffer_render = nullptr;
|
||||||
|
u64 buffer_size = 0;
|
||||||
|
u64 buffer_pitch = 0;
|
||||||
|
int set_id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct VideoConfigInternal {
|
struct VideoConfigInternal {
|
||||||
Lib::Mutex m_mutex;
|
Lib::Mutex m_mutex;
|
||||||
|
@ -16,6 +24,9 @@ struct VideoConfigInternal {
|
||||||
SceVideoOutVblankStatus m_vblank_status;
|
SceVideoOutVblankStatus m_vblank_status;
|
||||||
std::vector<HLE::Libs::LibKernel::EventQueues::SceKernelEqueue> m_flip_evtEq;
|
std::vector<HLE::Libs::LibKernel::EventQueues::SceKernelEqueue> m_flip_evtEq;
|
||||||
int m_flip_rate = 0;
|
int m_flip_rate = 0;
|
||||||
|
VideoOutBufferInfo buffers[16];
|
||||||
|
std::vector<VideoOutBufferSetInternal> buffers_sets;
|
||||||
|
int buffers_registration_index = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FlipQueue {
|
class FlipQueue {
|
||||||
|
@ -49,9 +60,19 @@ class VideoOutCtx {
|
||||||
int Open();
|
int Open();
|
||||||
VideoConfigInternal* getCtx(int handle);
|
VideoConfigInternal* getCtx(int handle);
|
||||||
FlipQueue& getFlipQueue() { return m_flip_queue; }
|
FlipQueue& getFlipQueue() { return m_flip_queue; }
|
||||||
|
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() {
|
||||||
|
Lib::LockMutexGuard lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_graphic_ctx == nullptr) {
|
||||||
|
m_graphic_ctx = Emulator::getGraphicCtx();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_graphic_ctx;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
Lib::Mutex m_mutex;
|
Lib::Mutex m_mutex;
|
||||||
VideoConfigInternal m_video_out_ctx;
|
VideoConfigInternal m_video_out_ctx;
|
||||||
FlipQueue m_flip_queue;
|
FlipQueue m_flip_queue;
|
||||||
|
HLE::Libs::Graphics::GraphicCtx* m_graphic_ctx = nullptr;
|
||||||
};
|
};
|
||||||
}; // namespace HLE::Graphics::Objects
|
}; // namespace HLE::Graphics::Objects
|
|
@ -1,9 +1,73 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
#include "Lib/Threads.h"
|
||||||
|
|
||||||
namespace HLE::Libs::Graphics {
|
namespace HLE::Libs::Graphics {
|
||||||
|
|
||||||
|
struct VulkanCommandPool {
|
||||||
|
Lib::Mutex mutex;
|
||||||
|
VkCommandPool pool = nullptr;
|
||||||
|
VkCommandBuffer* buffers = nullptr;
|
||||||
|
VkFence* fences = nullptr;
|
||||||
|
VkSemaphore* semaphores = nullptr;
|
||||||
|
bool* busy = nullptr;
|
||||||
|
u32 buffers_count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VulkanQueueInfo {
|
||||||
|
Lib::Mutex* mutex = nullptr;
|
||||||
|
u32 family = static_cast<u32>(-1);
|
||||||
|
u32 index = static_cast<u32>(-1);
|
||||||
|
VkQueue vk_queue = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
struct GraphicCtx {
|
struct GraphicCtx {
|
||||||
u32 screen_width = 0;
|
u32 screen_width = 0;
|
||||||
u32 screen_height = 0;
|
u32 screen_height = 0;
|
||||||
|
VkInstance m_instance = nullptr;
|
||||||
|
VkPhysicalDevice m_physical_device = nullptr;
|
||||||
|
VkDevice m_device = nullptr;
|
||||||
|
VulkanQueueInfo queues[11]; // VULKAN_QUEUES_NUM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class VulkanImageType { Unknown, VideoOut };
|
||||||
|
|
||||||
|
struct VulkanMemory {
|
||||||
|
VkMemoryRequirements requirements = {};
|
||||||
|
VkMemoryPropertyFlags property = 0;
|
||||||
|
VkDeviceMemory memory = nullptr;
|
||||||
|
VkDeviceSize offset = 0;
|
||||||
|
u32 type = 0;
|
||||||
|
u64 unique_id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VulkanBuffer {
|
||||||
|
VkBuffer buffer = nullptr;
|
||||||
|
VulkanMemory memory;
|
||||||
|
VkBufferUsageFlags usage = 0;
|
||||||
|
};
|
||||||
|
struct VulkanImage {
|
||||||
|
static constexpr int VIEW_MAX = 4;
|
||||||
|
static constexpr int VIEW_DEFAULT = 0;
|
||||||
|
static constexpr int VIEW_BGRA = 1;
|
||||||
|
static constexpr int VIEW_DEPTH_TEXTURE = 2;
|
||||||
|
|
||||||
|
explicit VulkanImage(VulkanImageType type) : type(type) {}
|
||||||
|
|
||||||
|
VulkanImageType type = VulkanImageType::Unknown;
|
||||||
|
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkExtent2D extent = {};
|
||||||
|
VkImage image = nullptr;
|
||||||
|
VkImageView image_view[VIEW_MAX] = {};
|
||||||
|
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
VulkanMemory memory;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VideoOutVulkanImage : public VulkanImage {
|
||||||
|
VideoOutVulkanImage() : VulkanImage(VulkanImageType::VideoOut) {}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace HLE::Libs::Graphics
|
} // namespace HLE::Libs::Graphics
|
|
@ -0,0 +1,232 @@
|
||||||
|
#include "graphics_render.h"
|
||||||
|
|
||||||
|
#include "Util/Singleton.h"
|
||||||
|
#include "emulator.h"
|
||||||
|
|
||||||
|
static thread_local GPU::CommandPool g_command_pool;
|
||||||
|
|
||||||
|
void GPU::renderCreateCtx() {
|
||||||
|
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||||
|
|
||||||
|
render_ctx->setGraphicCtx(Emulator::getGraphicCtx());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::CommandBuffer::allocateBuffer() {
|
||||||
|
m_pool = g_command_pool.getPool(m_queue);
|
||||||
|
|
||||||
|
Lib::LockMutexGuard lock(m_pool->mutex);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < m_pool->buffers_count; i++) {
|
||||||
|
if (!m_pool->busy[i]) {
|
||||||
|
m_pool->busy[i] = true;
|
||||||
|
vkResetCommandBuffer(m_pool->buffers[i], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
|
||||||
|
m_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::CommandBuffer::freeBuffer() {
|
||||||
|
Lib::LockMutexGuard lock(m_pool->mutex);
|
||||||
|
|
||||||
|
waitForFence();
|
||||||
|
|
||||||
|
m_pool->busy[m_index] = false;
|
||||||
|
vkResetCommandBuffer(m_pool->buffers[m_index], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
|
||||||
|
m_index = static_cast<uint32_t>(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::CommandBuffer::waitForFence() {
|
||||||
|
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||||
|
|
||||||
|
if (m_execute) {
|
||||||
|
auto* device = render_ctx->getGraphicCtx()->m_device;
|
||||||
|
|
||||||
|
vkWaitForFences(device, 1, &m_pool->fences[m_index], VK_TRUE, UINT64_MAX);
|
||||||
|
vkResetFences(device, 1, &m_pool->fences[m_index]);
|
||||||
|
|
||||||
|
m_execute = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void GPU::CommandBuffer::begin() const {
|
||||||
|
auto* buffer = m_pool->buffers[m_index];
|
||||||
|
|
||||||
|
VkCommandBufferBeginInfo begin_info{};
|
||||||
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
begin_info.pNext = nullptr;
|
||||||
|
begin_info.flags = 0;
|
||||||
|
begin_info.pInheritanceInfo = nullptr;
|
||||||
|
|
||||||
|
auto result = vkBeginCommandBuffer(buffer, &begin_info);
|
||||||
|
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
printf("vkBeginCommandBuffer failed\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void GPU::CommandBuffer::end() const {
|
||||||
|
auto* buffer = m_pool->buffers[m_index];
|
||||||
|
|
||||||
|
auto result = vkEndCommandBuffer(buffer);
|
||||||
|
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
printf("vkEndCommandBuffer failed\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void GPU::CommandBuffer::executeWithSemaphore() {
|
||||||
|
auto* buffer = m_pool->buffers[m_index];
|
||||||
|
auto* fence = m_pool->fences[m_index];
|
||||||
|
|
||||||
|
VkSubmitInfo submit_info{};
|
||||||
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
submit_info.pNext = nullptr;
|
||||||
|
submit_info.waitSemaphoreCount = 0;
|
||||||
|
submit_info.pWaitSemaphores = nullptr;
|
||||||
|
submit_info.pWaitDstStageMask = nullptr;
|
||||||
|
submit_info.commandBufferCount = 1;
|
||||||
|
submit_info.pCommandBuffers = &buffer;
|
||||||
|
submit_info.signalSemaphoreCount = 1;
|
||||||
|
submit_info.pSignalSemaphores = &m_pool->semaphores[m_index];
|
||||||
|
|
||||||
|
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||||
|
const auto& queue = render_ctx->getGraphicCtx()->queues[m_queue];
|
||||||
|
|
||||||
|
if (queue.mutex != nullptr) {
|
||||||
|
queue.mutex->LockMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = vkQueueSubmit(queue.vk_queue, 1, &submit_info, fence);
|
||||||
|
|
||||||
|
if (queue.mutex != nullptr) {
|
||||||
|
queue.mutex->LockMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_execute = true;
|
||||||
|
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
printf("vkQueueSubmit failed\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void GPU::CommandBuffer::execute() {
|
||||||
|
auto* buffer = m_pool->buffers[m_index];
|
||||||
|
auto* fence = m_pool->fences[m_index];
|
||||||
|
|
||||||
|
VkSubmitInfo submit_info{};
|
||||||
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
submit_info.pNext = nullptr;
|
||||||
|
submit_info.waitSemaphoreCount = 0;
|
||||||
|
submit_info.pWaitSemaphores = nullptr;
|
||||||
|
submit_info.pWaitDstStageMask = nullptr;
|
||||||
|
submit_info.commandBufferCount = 1;
|
||||||
|
submit_info.pCommandBuffers = &buffer;
|
||||||
|
submit_info.signalSemaphoreCount = 0;
|
||||||
|
submit_info.pSignalSemaphores = nullptr;
|
||||||
|
|
||||||
|
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||||
|
const auto& queue = render_ctx->getGraphicCtx()->queues[m_queue];
|
||||||
|
|
||||||
|
if (queue.mutex != nullptr) {
|
||||||
|
queue.mutex->LockMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = vkQueueSubmit(queue.vk_queue, 1, &submit_info, fence);
|
||||||
|
|
||||||
|
if (queue.mutex != nullptr) {
|
||||||
|
queue.mutex->UnlockMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_execute = true;
|
||||||
|
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
printf("vkQueueSubmit failed\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void GPU::CommandPool::createPool(int id) {
|
||||||
|
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||||
|
auto* ctx = render_ctx->getGraphicCtx();
|
||||||
|
|
||||||
|
m_pool[id] = new HLE::Libs::Graphics::VulkanCommandPool;
|
||||||
|
|
||||||
|
VkCommandPoolCreateInfo pool_info{};
|
||||||
|
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||||
|
pool_info.pNext = nullptr;
|
||||||
|
pool_info.queueFamilyIndex = ctx->queues[id].family;
|
||||||
|
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||||
|
|
||||||
|
vkCreateCommandPool(ctx->m_device, &pool_info, nullptr, &m_pool[id]->pool);
|
||||||
|
|
||||||
|
if (m_pool[id]->pool == nullptr) {
|
||||||
|
printf("pool is nullptr");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pool[id]->buffers_count = 4;
|
||||||
|
m_pool[id]->buffers = new VkCommandBuffer[m_pool[id]->buffers_count];
|
||||||
|
m_pool[id]->fences = new VkFence[m_pool[id]->buffers_count];
|
||||||
|
m_pool[id]->semaphores = new VkSemaphore[m_pool[id]->buffers_count];
|
||||||
|
m_pool[id]->busy = new bool[m_pool[id]->buffers_count];
|
||||||
|
|
||||||
|
VkCommandBufferAllocateInfo alloc_info{};
|
||||||
|
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
|
alloc_info.commandPool = m_pool[id]->pool;
|
||||||
|
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
alloc_info.commandBufferCount = m_pool[id]->buffers_count;
|
||||||
|
|
||||||
|
if (vkAllocateCommandBuffers(ctx->m_device, &alloc_info, m_pool[id]->buffers) != VK_SUCCESS) {
|
||||||
|
printf("Can't allocate command buffers\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < m_pool[id]->buffers_count; i++) {
|
||||||
|
m_pool[id]->busy[i] = false;
|
||||||
|
|
||||||
|
VkFenceCreateInfo fence_info{};
|
||||||
|
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||||
|
fence_info.pNext = nullptr;
|
||||||
|
fence_info.flags = 0;
|
||||||
|
|
||||||
|
if (vkCreateFence(ctx->m_device, &fence_info, nullptr, &m_pool[id]->fences[i]) != VK_SUCCESS) {
|
||||||
|
printf("Can't create fence\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSemaphoreCreateInfo semaphore_info{};
|
||||||
|
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||||
|
semaphore_info.pNext = nullptr;
|
||||||
|
semaphore_info.flags = 0;
|
||||||
|
|
||||||
|
if (vkCreateSemaphore(ctx->m_device, &semaphore_info, nullptr, &m_pool[id]->semaphores[i]) != VK_SUCCESS) {
|
||||||
|
printf("Can't create semas\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::CommandPool::deleteAllPool() {
|
||||||
|
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||||
|
auto* ctx = render_ctx->getGraphicCtx();
|
||||||
|
|
||||||
|
for (auto& pool : m_pool) {
|
||||||
|
if (pool != nullptr) {
|
||||||
|
for (uint32_t i = 0; i < pool->buffers_count; i++) {
|
||||||
|
vkDestroySemaphore(ctx->m_device, pool->semaphores[i], nullptr);
|
||||||
|
vkDestroyFence(ctx->m_device, pool->fences[i], nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkFreeCommandBuffers(ctx->m_device, pool->pool, pool->buffers_count, pool->buffers);
|
||||||
|
|
||||||
|
vkDestroyCommandPool(ctx->m_device, pool->pool, nullptr);
|
||||||
|
|
||||||
|
delete[] pool->semaphores;
|
||||||
|
delete[] pool->fences;
|
||||||
|
delete[] pool->buffers;
|
||||||
|
delete[] pool->busy;
|
||||||
|
|
||||||
|
delete pool;
|
||||||
|
pool = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
#pragma once
|
||||||
|
#include "graphics_ctx.h"
|
||||||
|
|
||||||
|
namespace GPU {
|
||||||
|
|
||||||
|
class CommandPool {
|
||||||
|
public:
|
||||||
|
CommandPool() = default;
|
||||||
|
~CommandPool() {}
|
||||||
|
|
||||||
|
HLE::Libs::Graphics::VulkanCommandPool* getPool(int id) {
|
||||||
|
if (m_pool[id] == nullptr) {
|
||||||
|
createPool(id);
|
||||||
|
}
|
||||||
|
return m_pool[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createPool(int id);
|
||||||
|
void deleteAllPool();
|
||||||
|
|
||||||
|
HLE::Libs::Graphics::VulkanCommandPool* m_pool[11] = {};
|
||||||
|
};
|
||||||
|
class CommandBuffer {
|
||||||
|
public:
|
||||||
|
explicit CommandBuffer(int queue) : m_queue(queue) { allocateBuffer(); }
|
||||||
|
virtual ~CommandBuffer() { freeBuffer(); }
|
||||||
|
void allocateBuffer();
|
||||||
|
void freeBuffer();
|
||||||
|
void waitForFence();
|
||||||
|
void begin() const;
|
||||||
|
void end() const;
|
||||||
|
void executeWithSemaphore();
|
||||||
|
void execute();
|
||||||
|
u32 getIndex() const { return m_index; }
|
||||||
|
HLE::Libs::Graphics::VulkanCommandPool* getPool() { return m_pool; }
|
||||||
|
private:
|
||||||
|
int m_queue = -1;
|
||||||
|
u32 m_index = static_cast<u32>(-1);
|
||||||
|
HLE::Libs::Graphics::VulkanCommandPool* m_pool = nullptr;
|
||||||
|
bool m_execute = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Framebuffer {
|
||||||
|
public:
|
||||||
|
Framebuffer() {}
|
||||||
|
virtual ~Framebuffer() {}
|
||||||
|
};
|
||||||
|
class RenderCtx {
|
||||||
|
public:
|
||||||
|
RenderCtx() : m_framebuffer(new Framebuffer) {}
|
||||||
|
|
||||||
|
virtual ~RenderCtx() {}
|
||||||
|
void setGraphicCtx(HLE::Libs::Graphics::GraphicCtx* ctx) { m_graphic_ctx = ctx; }
|
||||||
|
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() { return m_graphic_ctx; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Framebuffer* m_framebuffer = nullptr;
|
||||||
|
HLE::Libs::Graphics::GraphicCtx* m_graphic_ctx = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
void renderCreateCtx();
|
||||||
|
}; // namespace GPU
|
|
@ -1,8 +1,10 @@
|
||||||
#include "video_out.h"
|
#include "video_out.h"
|
||||||
|
|
||||||
|
#include <Core/PS4/GPU/video_out_buffer.h>
|
||||||
#include <Core/PS4/HLE/ErrorCodes.h>
|
#include <Core/PS4/HLE/ErrorCodes.h>
|
||||||
#include <Core/PS4/HLE/Libs.h>
|
#include <Core/PS4/HLE/Libs.h>
|
||||||
#include <Core/PS4/HLE/UserManagement/UsrMngCodes.h>
|
#include <Core/PS4/HLE/UserManagement/UsrMngCodes.h>
|
||||||
|
#include <Util/config.h>
|
||||||
#include <Util/log.h>
|
#include <Util/log.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -12,6 +14,9 @@
|
||||||
|
|
||||||
#include "Objects/video_out_ctx.h"
|
#include "Objects/video_out_ctx.h"
|
||||||
#include "Util/Singleton.h"
|
#include "Util/Singleton.h"
|
||||||
|
#include "emulator.h"
|
||||||
|
#include <Core/PS4/GPU/gpu_memory.h>
|
||||||
|
#include "graphics_render.h"
|
||||||
|
|
||||||
namespace HLE::Libs::Graphics::VideoOut {
|
namespace HLE::Libs::Graphics::VideoOut {
|
||||||
|
|
||||||
|
@ -115,9 +120,93 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(LibKernel::EventQueues::SceKernelEqueue
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses, s32 bufferNum,
|
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses, s32 bufferNum,
|
||||||
const SceVideoOutBufferAttribute* attribute) {
|
const SceVideoOutBufferAttribute* attribute) {
|
||||||
// BREAKPOINT();
|
PRINT_FUNCTION_NAME();
|
||||||
PRINT_DUMMY_FUNCTION_NAME();
|
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||||
return 0;
|
auto* ctx = videoOut->getCtx(handle);
|
||||||
|
|
||||||
|
if (handle == 1) { // main port
|
||||||
|
if (startIndex < 0 || startIndex > 15) {
|
||||||
|
LOG_TRACE_IF(log_file_videoout, "invalid startIndex = {}\n", startIndex);
|
||||||
|
return SCE_VIDEO_OUT_ERROR_INVALID_VALUE;
|
||||||
|
}
|
||||||
|
if (bufferNum < 1 || bufferNum > 16) {
|
||||||
|
LOG_TRACE_IF(log_file_videoout, "invalid bufferNum = {}\n", bufferNum);
|
||||||
|
return SCE_VIDEO_OUT_ERROR_INVALID_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addresses == nullptr) {
|
||||||
|
LOG_TRACE_IF(log_file_videoout, "addresses are null\n");
|
||||||
|
return SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute == nullptr) {
|
||||||
|
LOG_TRACE_IF(log_file_videoout, "attribute is null\n");
|
||||||
|
return SCE_VIDEO_OUT_ERROR_INVALID_OPTION;
|
||||||
|
}
|
||||||
|
if (attribute->aspectRatio != 0) {
|
||||||
|
LOG_TRACE_IF(log_file_videoout, "invalid aspect ratio = {}\n", attribute->aspectRatio);
|
||||||
|
return SCE_VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO;
|
||||||
|
}
|
||||||
|
if (attribute->tilingMode < 0 || attribute->tilingMode > 1) {
|
||||||
|
LOG_TRACE_IF(log_file_videoout, "invalid tilingMode = {}\n", attribute->tilingMode);
|
||||||
|
return SCE_VIDEO_OUT_ERROR_INVALID_TILING_MODE;
|
||||||
|
}
|
||||||
|
LOG_INFO_IF(log_file_videoout, "startIndex = {}\n", startIndex);
|
||||||
|
LOG_INFO_IF(log_file_videoout, "bufferNum = {}\n", bufferNum);
|
||||||
|
LOG_INFO_IF(log_file_videoout, "pixelFormat = {}\n", log_hex_full(attribute->pixelFormat));
|
||||||
|
LOG_INFO_IF(log_file_videoout, "tilingMode = {}\n", attribute->tilingMode);
|
||||||
|
LOG_INFO_IF(log_file_videoout, "aspectRatio = {}\n", attribute->aspectRatio);
|
||||||
|
LOG_INFO_IF(log_file_videoout, "width = {}\n", attribute->width);
|
||||||
|
LOG_INFO_IF(log_file_videoout, "height = {}\n", attribute->height);
|
||||||
|
LOG_INFO_IF(log_file_videoout, "pitchInPixel = {}\n", attribute->pitchInPixel);
|
||||||
|
LOG_INFO_IF(log_file_videoout, "option = {}\n", attribute->option);
|
||||||
|
|
||||||
|
int registration_index = ctx->buffers_registration_index++;
|
||||||
|
|
||||||
|
Emulator::checkAndWaitForGraphicsInit();
|
||||||
|
GPU::renderCreateCtx();
|
||||||
|
|
||||||
|
// try to calculate buffer size
|
||||||
|
u64 buffer_size = 1280 * 768 * 4; // TODO hardcoded value should be redone
|
||||||
|
u64 buffer_pitch = attribute->pitchInPixel;
|
||||||
|
|
||||||
|
VideoOutBufferSetInternal buf{};
|
||||||
|
|
||||||
|
buf.start_index = startIndex;
|
||||||
|
buf.num = bufferNum;
|
||||||
|
buf.set_id = registration_index;
|
||||||
|
buf.attr = *attribute;
|
||||||
|
|
||||||
|
ctx->buffers_sets.push_back(buf);
|
||||||
|
|
||||||
|
GPU::VideoOutBufferFormat format = GPU::VideoOutBufferFormat::Unknown;
|
||||||
|
|
||||||
|
if (attribute->pixelFormat == 0x80000000) {
|
||||||
|
format = GPU::VideoOutBufferFormat::B8G8R8A8Srgb;
|
||||||
|
} else if (attribute->pixelFormat == 0x80002200) {
|
||||||
|
format = GPU::VideoOutBufferFormat::R8G8B8A8Srgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPU::VideoOutBufferObj buffer_info(format, attribute->width, attribute->height, attribute->tilingMode == 0, Config::isNeoMode(), buffer_pitch);
|
||||||
|
|
||||||
|
for (int i = 0; i < bufferNum; i++) {
|
||||||
|
if (ctx->buffers[i + startIndex].buffer != nullptr) {
|
||||||
|
LOG_TRACE_IF(log_file_videoout, "buffer slot {} is occupied!\n", i + startIndex);
|
||||||
|
return SCE_VIDEO_OUT_ERROR_SLOT_OCCUPIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->buffers[i + startIndex].set_id = registration_index;
|
||||||
|
ctx->buffers[i + startIndex].buffer = addresses[i];
|
||||||
|
ctx->buffers[i + startIndex].buffer_size = buffer_size;
|
||||||
|
ctx->buffers[i + startIndex].buffer_pitch = buffer_pitch;
|
||||||
|
ctx->buffers[i + startIndex].buffer_render = static_cast<Graphics::VideoOutVulkanImage*>(
|
||||||
|
GPU::memoryCreateObj(
|
||||||
|
0, videoOut->getGraphicCtx(), nullptr, reinterpret_cast<uint64_t>(addresses[i]), buffer_size, buffer_info));
|
||||||
|
|
||||||
|
LOG_INFO_IF(log_file_videoout, "buffers[{}] = {}\n", i + startIndex, log_hex_full(reinterpret_cast<uint64_t>(addresses[i])));
|
||||||
|
}
|
||||||
|
|
||||||
|
return registration_index;
|
||||||
}
|
}
|
||||||
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) {
|
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) {
|
||||||
PRINT_FUNCTION_NAME();
|
PRINT_FUNCTION_NAME();
|
||||||
|
|
|
@ -87,6 +87,13 @@ struct SceVideoOutVblankStatus {
|
||||||
u08 pad1[7] = {};
|
u08 pad1[7] = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct VideoOutBufferSetInternal {
|
||||||
|
SceVideoOutBufferAttribute attr;
|
||||||
|
int start_index = 0;
|
||||||
|
int num = 0;
|
||||||
|
int set_id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
void videoOutInit(u32 width, u32 height);
|
void videoOutInit(u32 width, u32 height);
|
||||||
std::string getPixelFormatString(s32 format);
|
std::string getPixelFormatString(s32 format);
|
||||||
void videoOutRegisterLib(SymbolsResolver* sym);
|
void videoOutRegisterLib(SymbolsResolver* sym);
|
||||||
|
|
|
@ -120,7 +120,7 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gpu_mode != GPU::MemoryMode::NoAccess) {
|
if (gpu_mode != GPU::MemoryMode::NoAccess) {
|
||||||
GPU::MemorySetAllocArea(out_addr, len);
|
GPU::memorySetAllocArea(out_addr, len);
|
||||||
}
|
}
|
||||||
return SCE_OK;
|
return SCE_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "../Loader/Elf.h"
|
#include "../Loader/Elf.h"
|
||||||
#include <Util/log.h>
|
#include <Util/log.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
#include <Core/PS4/GPU/gpu_memory.h>
|
||||||
|
#include <emulator.h>
|
||||||
|
|
||||||
namespace HLE::Libs::LibSceGnmDriver {
|
namespace HLE::Libs::LibSceGnmDriver {
|
||||||
|
|
||||||
|
@ -88,7 +90,8 @@ namespace HLE::Libs::LibSceGnmDriver {
|
||||||
void sceGnmDriverTraceInProgress(){}
|
void sceGnmDriverTraceInProgress(){}
|
||||||
void sceGnmDriverTriggerCapture(){}
|
void sceGnmDriverTriggerCapture(){}
|
||||||
void sceGnmEndWorkload(){}
|
void sceGnmEndWorkload(){}
|
||||||
void sceGnmFlushGarlic() { PRINT_DUMMY_FUNCTION_NAME();
|
void sceGnmFlushGarlic() { PRINT_FUNCTION_NAME();
|
||||||
|
GPU::flushGarlic(Emulator::getGraphicCtx());
|
||||||
}
|
}
|
||||||
void sceGnmGetEqEventType(){}
|
void sceGnmGetEqEventType(){}
|
||||||
void sceGnmGetEqTimeStamp(){}
|
void sceGnmGetEqTimeStamp(){}
|
||||||
|
|
136
src/emulator.cpp
136
src/emulator.cpp
|
@ -1,19 +1,30 @@
|
||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
|
|
||||||
|
#include <Core/PS4/HLE/Graphics/graphics_render.h>
|
||||||
|
#include <Util/Singleton.h>
|
||||||
|
#include <vulkan_util.h>
|
||||||
|
|
||||||
#include "Core/PS4/HLE/Graphics/video_out.h"
|
#include "Core/PS4/HLE/Graphics/video_out.h"
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
|
|
||||||
static WindowCtx* g_window_ctx = nullptr;
|
|
||||||
bool m_emu_needs_exit = false;
|
bool m_emu_needs_exit = false;
|
||||||
|
|
||||||
void emuInit(u32 width, u32 height) {
|
void emuInit(u32 width, u32 height) {
|
||||||
g_window_ctx = new WindowCtx;
|
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||||
|
|
||||||
g_window_ctx->m_graphic_ctx.screen_width = width;
|
window_ctx->m_graphic_ctx.screen_width = width;
|
||||||
g_window_ctx->m_graphic_ctx.screen_height = height;
|
window_ctx->m_graphic_ctx.screen_height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkAndWaitForGraphicsInit() {
|
||||||
|
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||||
|
Lib::LockMutexGuard lock(window_ctx->m_mutex);
|
||||||
|
|
||||||
|
while (!window_ctx->m_is_graphic_initialized) {
|
||||||
|
window_ctx->m_graphic_initialized_cond.WaitCondVar(&window_ctx->m_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
static void CreateSdlWindow(WindowCtx* ctx) {
|
static void CreateSdlWindow(WindowCtx* ctx) {
|
||||||
int width = static_cast<int>(ctx->m_graphic_ctx.screen_width);
|
int width = static_cast<int>(ctx->m_graphic_ctx.screen_width);
|
||||||
int height = static_cast<int>(ctx->m_graphic_ctx.screen_height);
|
int height = static_cast<int>(ctx->m_graphic_ctx.screen_height);
|
||||||
|
@ -34,17 +45,18 @@ static void CreateSdlWindow(WindowCtx* ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetWindowResizable(ctx->m_window, SDL_FALSE); // we don't support resizable atm
|
SDL_SetWindowResizable(ctx->m_window, SDL_FALSE); // we don't support resizable atm
|
||||||
SDL_ShowWindow(g_window_ctx->m_window); // TODO should be removed just left it over to make it fancy :D
|
|
||||||
}
|
}
|
||||||
void emuRun() {
|
void emuRun() {
|
||||||
g_window_ctx->m_mutex.LockMutex();
|
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||||
|
window_ctx->m_mutex.LockMutex();
|
||||||
{
|
{
|
||||||
// init window and wait until init finishes
|
// init window and wait until init finishes
|
||||||
CreateSdlWindow(g_window_ctx);
|
CreateSdlWindow(window_ctx);
|
||||||
g_window_ctx->m_is_graphic_initialized = true;
|
Graphics::Vulkan::vulkanCreate(window_ctx);
|
||||||
g_window_ctx->m_graphic_initialized_cond.SignalCondVar();
|
window_ctx->m_is_graphic_initialized = true;
|
||||||
|
window_ctx->m_graphic_initialized_cond.SignalCondVar();
|
||||||
}
|
}
|
||||||
g_window_ctx->m_mutex.UnlockMutex();
|
window_ctx->m_mutex.UnlockMutex();
|
||||||
|
|
||||||
bool exit_loop = false;
|
bool exit_loop = false;
|
||||||
|
|
||||||
|
@ -81,4 +93,108 @@ void emuRun() {
|
||||||
}
|
}
|
||||||
std::exit(0);
|
std::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() {
|
||||||
|
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||||
|
Lib::LockMutexGuard lock(window_ctx->m_mutex);
|
||||||
|
|
||||||
|
return &window_ctx->m_graphic_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image) {
|
||||||
|
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||||
|
if (window_ctx->is_window_hidden) {
|
||||||
|
SDL_ShowWindow(window_ctx->m_window);
|
||||||
|
window_ctx->is_window_hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window_ctx->swapchain->current_index = static_cast<u32>(-1);
|
||||||
|
|
||||||
|
auto result = vkAcquireNextImageKHR(window_ctx->m_graphic_ctx.m_device, window_ctx->swapchain->swapchain, UINT64_MAX, nullptr,
|
||||||
|
window_ctx->swapchain->present_complete_fence, &window_ctx->swapchain->current_index);
|
||||||
|
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
printf("Can't aquireNextImage\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
if (window_ctx->swapchain->current_index == static_cast<u32>(-1)) {
|
||||||
|
printf("Unsupported:swapchain current index is -1\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
result = vkWaitForFences(window_ctx->m_graphic_ctx.m_device, 1, &window_ctx->swapchain->present_complete_fence, VK_TRUE, 100000000);
|
||||||
|
} while (result == VK_TIMEOUT);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
printf("vkWaitForFences is not success\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkResetFences(window_ctx->m_graphic_ctx.m_device, 1, &window_ctx->swapchain->present_complete_fence);
|
||||||
|
|
||||||
|
auto* blt_src_image = image;
|
||||||
|
auto* blt_dst_image = window_ctx->swapchain;
|
||||||
|
|
||||||
|
if (blt_src_image == nullptr) {
|
||||||
|
printf("blt_src_image is null\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
if (blt_dst_image == nullptr) {
|
||||||
|
printf("blt_dst_image is null\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPU::CommandBuffer buffer(10);
|
||||||
|
|
||||||
|
auto* vk_buffer = buffer.getPool()->buffers[buffer.getIndex()];
|
||||||
|
|
||||||
|
buffer.begin();
|
||||||
|
|
||||||
|
Graphics::Vulkan::vulkanBlitImage(&buffer, blt_src_image, blt_dst_image);
|
||||||
|
|
||||||
|
VkImageMemoryBarrier pre_present_barrier{};
|
||||||
|
pre_present_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
pre_present_barrier.pNext = nullptr;
|
||||||
|
pre_present_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
pre_present_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||||
|
pre_present_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
pre_present_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||||
|
pre_present_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
pre_present_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
pre_present_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
pre_present_barrier.subresourceRange.baseMipLevel = 0;
|
||||||
|
pre_present_barrier.subresourceRange.levelCount = 1;
|
||||||
|
pre_present_barrier.subresourceRange.baseArrayLayer = 0;
|
||||||
|
pre_present_barrier.subresourceRange.layerCount = 1;
|
||||||
|
pre_present_barrier.image = window_ctx->swapchain->swapchain_images[window_ctx->swapchain->current_index];
|
||||||
|
vkCmdPipelineBarrier(vk_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
||||||
|
&pre_present_barrier);
|
||||||
|
|
||||||
|
buffer.end();
|
||||||
|
buffer.executeWithSemaphore();
|
||||||
|
|
||||||
|
VkPresentInfoKHR present{};
|
||||||
|
present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||||
|
present.pNext = nullptr;
|
||||||
|
present.swapchainCount = 1;
|
||||||
|
present.pSwapchains = &window_ctx->swapchain->swapchain;
|
||||||
|
present.pImageIndices = &window_ctx->swapchain->current_index;
|
||||||
|
present.pWaitSemaphores = &buffer.getPool()->semaphores[buffer.getIndex()];
|
||||||
|
present.waitSemaphoreCount = 1;
|
||||||
|
present.pResults = nullptr;
|
||||||
|
|
||||||
|
const auto& queue = window_ctx->m_graphic_ctx.queues[10];
|
||||||
|
|
||||||
|
if (queue.mutex != nullptr) {
|
||||||
|
printf("queue.mutexe is null\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = vkQueuePresentKHR(queue.vk_queue, &present);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
printf("vkQueuePresentKHR failed\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Emulator
|
} // namespace Emulator
|
|
@ -3,7 +3,58 @@
|
||||||
#include <Lib/Threads.h>
|
#include <Lib/Threads.h>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
|
|
||||||
|
struct VulkanExt {
|
||||||
|
bool enable_validation_layers = false;
|
||||||
|
|
||||||
|
std::vector<const char*> required_extensions;
|
||||||
|
std::vector<VkExtensionProperties> available_extensions;
|
||||||
|
std::vector<const char*> required_layers;
|
||||||
|
std::vector<VkLayerProperties> available_layers;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VulkanSurfaceCapabilities {
|
||||||
|
VkSurfaceCapabilitiesKHR capabilities{};
|
||||||
|
std::vector<VkSurfaceFormatKHR> formats;
|
||||||
|
std::vector<VkPresentModeKHR> present_modes;
|
||||||
|
bool is_format_srgb_bgra32 = false;
|
||||||
|
bool is_format_unorm_bgra32 = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VulkanQueueInfo {
|
||||||
|
u32 family = 0;
|
||||||
|
u32 index = 0;
|
||||||
|
bool is_graphics = false;
|
||||||
|
bool is_compute = false;
|
||||||
|
bool is_transfer = false;
|
||||||
|
bool is_present = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VulkanQueues {
|
||||||
|
u32 family_count = 0;
|
||||||
|
std::vector<VulkanQueueInfo> available;
|
||||||
|
std::vector<VulkanQueueInfo> graphics;
|
||||||
|
std::vector<VulkanQueueInfo> compute;
|
||||||
|
std::vector<VulkanQueueInfo> transfer;
|
||||||
|
std::vector<VulkanQueueInfo> present;
|
||||||
|
std::vector<u32> family_used;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VulkanSwapchain {
|
||||||
|
VkSwapchainKHR swapchain = nullptr;
|
||||||
|
VkFormat swapchain_format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkExtent2D swapchain_extent = {};
|
||||||
|
VkImage* swapchain_images = nullptr;
|
||||||
|
VkImageView* swapchain_image_views = nullptr;
|
||||||
|
u32 swapchain_images_count = 0;
|
||||||
|
VkSemaphore present_complete_semaphore = nullptr;
|
||||||
|
VkFence present_complete_fence = nullptr;
|
||||||
|
u32 current_index = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct WindowCtx {
|
struct WindowCtx {
|
||||||
HLE::Libs::Graphics::GraphicCtx m_graphic_ctx;
|
HLE::Libs::Graphics::GraphicCtx m_graphic_ctx;
|
||||||
Lib::Mutex m_mutex;
|
Lib::Mutex m_mutex;
|
||||||
|
@ -11,6 +62,9 @@ struct WindowCtx {
|
||||||
Lib::ConditionVariable m_graphic_initialized_cond;
|
Lib::ConditionVariable m_graphic_initialized_cond;
|
||||||
SDL_Window* m_window = nullptr;
|
SDL_Window* m_window = nullptr;
|
||||||
bool is_window_hidden = true;
|
bool is_window_hidden = true;
|
||||||
|
VkSurfaceKHR m_surface = nullptr;
|
||||||
|
VulkanSurfaceCapabilities* m_surface_capabilities = nullptr;
|
||||||
|
VulkanSwapchain* swapchain = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EmuPrivate {
|
struct EmuPrivate {
|
||||||
|
@ -24,4 +78,7 @@ struct EmuPrivate {
|
||||||
};
|
};
|
||||||
void emuInit(u32 width, u32 height);
|
void emuInit(u32 width, u32 height);
|
||||||
void emuRun();
|
void emuRun();
|
||||||
|
void checkAndWaitForGraphicsInit();
|
||||||
|
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx();
|
||||||
|
void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image);
|
||||||
} // namespace Emulator
|
} // namespace Emulator
|
|
@ -0,0 +1,604 @@
|
||||||
|
#include "vulkan_util.h"
|
||||||
|
|
||||||
|
#include <Core/PS4/GPU/gpu_memory.h>
|
||||||
|
#include <SDL_vulkan.h>
|
||||||
|
#include <Util/Singleton.h>
|
||||||
|
#include <Util/log.h>
|
||||||
|
#include <debug.h>
|
||||||
|
#include <vulkan/vk_enum_string_helper.h>
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
constexpr bool log_file_vulkanutil = true; // disable it to disable logging
|
||||||
|
|
||||||
|
void Graphics::Vulkan::vulkanCreate(Emulator::WindowCtx* ctx) {
|
||||||
|
Emulator::VulkanExt ext;
|
||||||
|
vulkanGetInstanceExtensions(&ext);
|
||||||
|
|
||||||
|
VkApplicationInfo app_info{};
|
||||||
|
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
|
app_info.pNext = nullptr;
|
||||||
|
app_info.pApplicationName = "shadps4";
|
||||||
|
app_info.applicationVersion = 1;
|
||||||
|
app_info.pEngineName = "shadps4";
|
||||||
|
app_info.engineVersion = 1;
|
||||||
|
app_info.apiVersion = VK_API_VERSION_1_2;
|
||||||
|
|
||||||
|
VkInstanceCreateInfo inst_info{};
|
||||||
|
inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||||
|
inst_info.pNext = nullptr;
|
||||||
|
inst_info.flags = 0;
|
||||||
|
inst_info.pApplicationInfo = &app_info;
|
||||||
|
inst_info.enabledExtensionCount = ext.required_extensions.size();
|
||||||
|
inst_info.ppEnabledExtensionNames = ext.required_extensions.data();
|
||||||
|
inst_info.enabledLayerCount = 0;
|
||||||
|
inst_info.ppEnabledLayerNames = nullptr;
|
||||||
|
|
||||||
|
VkResult result = vkCreateInstance(&inst_info, nullptr, &ctx->m_graphic_ctx.m_instance);
|
||||||
|
if (result == VK_ERROR_INCOMPATIBLE_DRIVER) {
|
||||||
|
LOG_CRITICAL_IF(log_file_vulkanutil, "Can't find an compatiblie vulkan driver\n");
|
||||||
|
std::exit(0);
|
||||||
|
} else if (result != VK_SUCCESS) {
|
||||||
|
LOG_CRITICAL_IF(log_file_vulkanutil, "Can't create an vulkan instance\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SDL_Vulkan_CreateSurface(ctx->m_window, ctx->m_graphic_ctx.m_instance, &ctx->m_surface) == SDL_FALSE) {
|
||||||
|
LOG_CRITICAL_IF(log_file_vulkanutil, "Can't create an vulkan surface\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO i am not sure if it's that it is neccesary or if it needs more
|
||||||
|
std::vector<const char*> device_extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
|
||||||
|
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME, "VK_KHR_maintenance1"};
|
||||||
|
|
||||||
|
ctx->m_surface_capabilities = new Emulator::VulkanSurfaceCapabilities{};
|
||||||
|
Emulator::VulkanQueues queues;
|
||||||
|
|
||||||
|
vulkanFindCompatiblePhysicalDevice(ctx->m_graphic_ctx.m_instance, ctx->m_surface, device_extensions, ctx->m_surface_capabilities,
|
||||||
|
&ctx->m_graphic_ctx.m_physical_device, &queues);
|
||||||
|
|
||||||
|
if (ctx->m_graphic_ctx.m_physical_device == nullptr) {
|
||||||
|
LOG_CRITICAL_IF(log_file_vulkanutil, "Can't find compatible vulkan device\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceProperties device_properties{};
|
||||||
|
vkGetPhysicalDeviceProperties(ctx->m_graphic_ctx.m_physical_device, &device_properties);
|
||||||
|
|
||||||
|
LOG_INFO_IF(log_file_vulkanutil, "GFX device to be used : {}\n", device_properties.deviceName);
|
||||||
|
|
||||||
|
ctx->m_graphic_ctx.m_device = vulkanCreateDevice(ctx->m_graphic_ctx.m_physical_device, ctx->m_surface, &ext, queues, device_extensions);
|
||||||
|
if (ctx->m_graphic_ctx.m_device == nullptr) {
|
||||||
|
LOG_CRITICAL_IF(log_file_vulkanutil, "Can't create vulkan device\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vulkanCreateQueues(&ctx->m_graphic_ctx, queues);
|
||||||
|
ctx->swapchain = vulkanCreateSwapchain(&ctx->m_graphic_ctx, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Emulator::VulkanSwapchain* Graphics::Vulkan::vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count) {
|
||||||
|
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||||
|
Lib::LockMutexGuard lock(window_ctx->m_mutex);
|
||||||
|
|
||||||
|
auto* s = new Emulator::VulkanSwapchain;
|
||||||
|
|
||||||
|
VkExtent2D extent{};
|
||||||
|
extent.width = clamp(ctx->screen_width, window_ctx->m_surface_capabilities->capabilities.minImageExtent.width,
|
||||||
|
window_ctx->m_surface_capabilities->capabilities.maxImageExtent.width);
|
||||||
|
extent.height = clamp(ctx->screen_height, window_ctx->m_surface_capabilities->capabilities.minImageExtent.height,
|
||||||
|
window_ctx->m_surface_capabilities->capabilities.maxImageExtent.height);
|
||||||
|
|
||||||
|
image_count = clamp(image_count, window_ctx->m_surface_capabilities->capabilities.minImageCount,
|
||||||
|
window_ctx->m_surface_capabilities->capabilities.maxImageCount);
|
||||||
|
|
||||||
|
VkSwapchainCreateInfoKHR create_info{};
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||||
|
create_info.pNext = nullptr;
|
||||||
|
create_info.flags = 0;
|
||||||
|
create_info.surface = window_ctx->m_surface;
|
||||||
|
create_info.minImageCount = image_count;
|
||||||
|
|
||||||
|
if (window_ctx->m_surface_capabilities->is_format_unorm_bgra32) {
|
||||||
|
create_info.imageFormat = VK_FORMAT_B8G8R8A8_UNORM;
|
||||||
|
create_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||||
|
} else if (window_ctx->m_surface_capabilities->is_format_srgb_bgra32) {
|
||||||
|
create_info.imageFormat = VK_FORMAT_B8G8R8A8_SRGB;
|
||||||
|
create_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||||
|
} else {
|
||||||
|
create_info.imageFormat = window_ctx->m_surface_capabilities->formats.at(0).format;
|
||||||
|
create_info.imageColorSpace = window_ctx->m_surface_capabilities->formats.at(0).colorSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_info.imageExtent = extent;
|
||||||
|
create_info.imageArrayLayers = 1;
|
||||||
|
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
create_info.queueFamilyIndexCount = 0;
|
||||||
|
create_info.pQueueFamilyIndices = nullptr;
|
||||||
|
create_info.preTransform = window_ctx->m_surface_capabilities->capabilities.currentTransform;
|
||||||
|
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
create_info.clipped = VK_TRUE;
|
||||||
|
create_info.oldSwapchain = nullptr;
|
||||||
|
|
||||||
|
s->swapchain_format = create_info.imageFormat;
|
||||||
|
s->swapchain_extent = extent;
|
||||||
|
|
||||||
|
vkCreateSwapchainKHR(ctx->m_device, &create_info, nullptr, &s->swapchain);
|
||||||
|
|
||||||
|
vkGetSwapchainImagesKHR(ctx->m_device, s->swapchain, &s->swapchain_images_count, nullptr);
|
||||||
|
|
||||||
|
s->swapchain_images = new VkImage[s->swapchain_images_count];
|
||||||
|
vkGetSwapchainImagesKHR(ctx->m_device, s->swapchain, &s->swapchain_images_count, s->swapchain_images);
|
||||||
|
|
||||||
|
s->swapchain_image_views = new VkImageView[s->swapchain_images_count];
|
||||||
|
for (uint32_t i = 0; i < s->swapchain_images_count; i++) {
|
||||||
|
VkImageViewCreateInfo create_info{};
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
create_info.pNext = nullptr;
|
||||||
|
create_info.flags = 0;
|
||||||
|
create_info.image = (s->swapchain_images)[i];
|
||||||
|
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
create_info.format = s->swapchain_format;
|
||||||
|
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
create_info.subresourceRange.baseArrayLayer = 0;
|
||||||
|
create_info.subresourceRange.baseMipLevel = 0;
|
||||||
|
create_info.subresourceRange.layerCount = 1;
|
||||||
|
create_info.subresourceRange.levelCount = 1;
|
||||||
|
|
||||||
|
vkCreateImageView(ctx->m_device, &create_info, nullptr, &((s->swapchain_image_views)[i]));
|
||||||
|
}
|
||||||
|
if (s->swapchain == nullptr) {
|
||||||
|
LOG_CRITICAL_IF(log_file_vulkanutil, "Could not create swapchain\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->current_index = static_cast<uint32_t>(-1);
|
||||||
|
|
||||||
|
VkSemaphoreCreateInfo present_complete_info{};
|
||||||
|
present_complete_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||||
|
present_complete_info.pNext = nullptr;
|
||||||
|
present_complete_info.flags = 0;
|
||||||
|
|
||||||
|
auto result = vkCreateSemaphore(ctx->m_device, &present_complete_info, nullptr, &s->present_complete_semaphore);
|
||||||
|
|
||||||
|
VkFenceCreateInfo fence_info{};
|
||||||
|
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||||
|
fence_info.pNext = nullptr;
|
||||||
|
fence_info.flags = 0;
|
||||||
|
|
||||||
|
result = vkCreateFence(ctx->m_device, &fence_info, nullptr, &s->present_complete_fence);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
LOG_CRITICAL_IF(log_file_vulkanutil, "Can't create vulkan fence\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Vulkan::vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emulator::VulkanQueues& queues) {
|
||||||
|
auto get_queue = [ctx](int id, const Emulator::VulkanQueueInfo& info, bool with_mutex = false) {
|
||||||
|
ctx->queues[id].family = info.family;
|
||||||
|
ctx->queues[id].index = info.index;
|
||||||
|
vkGetDeviceQueue(ctx->m_device, ctx->queues[id].family, ctx->queues[id].index, &ctx->queues[id].vk_queue);
|
||||||
|
if (with_mutex) {
|
||||||
|
ctx->queues[id].mutex = new Lib::Mutex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
get_queue(VULKAN_QUEUE_GFX, queues.graphics.at(0));
|
||||||
|
get_queue(VULKAN_QUEUE_UTIL, queues.transfer.at(0));
|
||||||
|
get_queue(VULKAN_QUEUE_PRESENT, queues.present.at(0));
|
||||||
|
|
||||||
|
for (int id = 0; id < VULKAN_QUEUE_COMPUTE_NUM; id++) {
|
||||||
|
bool with_mutex = (VULKAN_QUEUE_COMPUTE_NUM == queues.compute.size());
|
||||||
|
get_queue(id, queues.compute.at(id % queues.compute.size()), with_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDevice Graphics::Vulkan::vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emulator::VulkanExt* r,
|
||||||
|
const Emulator::VulkanQueues& queues, const std::vector<const char*>& device_extensions) {
|
||||||
|
std::vector<VkDeviceQueueCreateInfo> queue_create_info(queues.family_count);
|
||||||
|
std::vector<std::vector<float>> queue_priority(queues.family_count);
|
||||||
|
uint32_t queue_create_info_num = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < queues.family_count; i++) {
|
||||||
|
if (queues.family_used[i] != 0) {
|
||||||
|
for (uint32_t pi = 0; pi < queues.family_used[i]; pi++) {
|
||||||
|
queue_priority[queue_create_info_num].push_back(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_create_info[queue_create_info_num].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
queue_create_info[queue_create_info_num].pNext = nullptr;
|
||||||
|
queue_create_info[queue_create_info_num].flags = 0;
|
||||||
|
queue_create_info[queue_create_info_num].queueFamilyIndex = i;
|
||||||
|
queue_create_info[queue_create_info_num].queueCount = queues.family_used[i];
|
||||||
|
queue_create_info[queue_create_info_num].pQueuePriorities = queue_priority[queue_create_info_num].data();
|
||||||
|
|
||||||
|
queue_create_info_num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures device_features{};
|
||||||
|
// TODO add neccesary device features
|
||||||
|
|
||||||
|
VkDeviceCreateInfo create_info{};
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
|
create_info.pNext = nullptr;
|
||||||
|
create_info.flags = 0;
|
||||||
|
create_info.pQueueCreateInfos = queue_create_info.data();
|
||||||
|
create_info.queueCreateInfoCount = queue_create_info_num;
|
||||||
|
create_info.enabledLayerCount = 0;
|
||||||
|
create_info.ppEnabledLayerNames = nullptr;
|
||||||
|
create_info.enabledExtensionCount = device_extensions.size();
|
||||||
|
create_info.ppEnabledExtensionNames = device_extensions.data();
|
||||||
|
create_info.pEnabledFeatures = &device_features;
|
||||||
|
|
||||||
|
VkDevice device = nullptr;
|
||||||
|
|
||||||
|
vkCreateDevice(physical_device, &create_info, nullptr, &device);
|
||||||
|
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
void Graphics::Vulkan::vulkanGetInstanceExtensions(Emulator::VulkanExt* ext) {
|
||||||
|
u32 required_extensions_count = 0;
|
||||||
|
u32 available_extensions_count = 0;
|
||||||
|
u32 available_layers_count = 0;
|
||||||
|
auto result = SDL_Vulkan_GetInstanceExtensions(&required_extensions_count, nullptr);
|
||||||
|
|
||||||
|
ext->required_extensions = std::vector<const char*>(required_extensions_count);
|
||||||
|
|
||||||
|
result = SDL_Vulkan_GetInstanceExtensions(&required_extensions_count, ext->required_extensions.data());
|
||||||
|
|
||||||
|
vkEnumerateInstanceExtensionProperties(nullptr, &available_extensions_count, nullptr);
|
||||||
|
|
||||||
|
ext->available_extensions = std::vector<VkExtensionProperties>(available_extensions_count);
|
||||||
|
|
||||||
|
vkEnumerateInstanceExtensionProperties(nullptr, &available_extensions_count, ext->available_extensions.data());
|
||||||
|
|
||||||
|
vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr);
|
||||||
|
ext->available_layers = std::vector<VkLayerProperties>(available_layers_count);
|
||||||
|
vkEnumerateInstanceLayerProperties(&available_layers_count, ext->available_layers.data());
|
||||||
|
|
||||||
|
for (const char* ext : ext->required_extensions) {
|
||||||
|
LOG_INFO_IF(log_file_vulkanutil, "Vulkan required extension = {}\n", ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& ext : ext->available_extensions) {
|
||||||
|
LOG_INFO_IF(log_file_vulkanutil, "Vulkan available extension: {}, version = {}\n", ext.extensionName, ext.specVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& l : ext->available_layers) {
|
||||||
|
LOG_INFO_IF(log_file_vulkanutil, "Vulkan available layer: {}, specVersion = {}, implVersion = {}, {}\n", l.layerName, l.specVersion,
|
||||||
|
l.implementationVersion, l.description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, VkSurfaceKHR surface,
|
||||||
|
const std::vector<const char*>& device_extensions,
|
||||||
|
Emulator::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
||||||
|
Emulator::VulkanQueues* out_queues) {
|
||||||
|
u32 count_devices = 0;
|
||||||
|
vkEnumeratePhysicalDevices(instance, &count_devices, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkPhysicalDevice> devices(count_devices);
|
||||||
|
vkEnumeratePhysicalDevices(instance, &count_devices, devices.data());
|
||||||
|
|
||||||
|
VkPhysicalDevice found_best_device = nullptr;
|
||||||
|
Emulator::VulkanQueues found_best_queues;
|
||||||
|
|
||||||
|
for (const auto& device : devices) {
|
||||||
|
VkPhysicalDeviceProperties device_properties{};
|
||||||
|
VkPhysicalDeviceFeatures2 device_features2{};
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceProperties(device, &device_properties);
|
||||||
|
vkGetPhysicalDeviceFeatures2(device, &device_features2);
|
||||||
|
if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) {
|
||||||
|
continue; // we don't want integrated gpu for now .Later we will check the requirements and see what we can support (TODO fix me)
|
||||||
|
}
|
||||||
|
LOG_INFO_IF(log_file_vulkanutil, "Vulkan device: {}\n", device_properties.deviceName);
|
||||||
|
|
||||||
|
auto qs = vulkanFindQueues(device, surface);
|
||||||
|
|
||||||
|
vulkanGetSurfaceCapabilities(device, surface, out_capabilities);
|
||||||
|
|
||||||
|
found_best_device = device;
|
||||||
|
found_best_queues = qs;
|
||||||
|
}
|
||||||
|
*out_device = found_best_device;
|
||||||
|
*out_queues = found_best_queues;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emulator::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
|
Emulator::VulkanQueues qs;
|
||||||
|
|
||||||
|
u32 queue_family_count = 0;
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, nullptr);
|
||||||
|
std::vector<VkQueueFamilyProperties> queue_families(queue_family_count);
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data());
|
||||||
|
|
||||||
|
qs.family_count = queue_family_count;
|
||||||
|
|
||||||
|
u32 family = 0;
|
||||||
|
for (auto& f : queue_families) {
|
||||||
|
VkBool32 presentation_supported = VK_FALSE;
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, family, surface, &presentation_supported);
|
||||||
|
|
||||||
|
LOG_INFO_IF(log_file_vulkanutil, "queue family: {}, count = {}, present = {}\n", string_VkQueueFlags(f.queueFlags).c_str(), f.queueCount,
|
||||||
|
(presentation_supported == VK_TRUE ? "true" : "false"));
|
||||||
|
for (uint32_t i = 0; i < f.queueCount; i++) {
|
||||||
|
Emulator::VulkanQueueInfo info;
|
||||||
|
info.family = family;
|
||||||
|
info.index = i;
|
||||||
|
info.is_graphics = (f.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0;
|
||||||
|
info.is_compute = (f.queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
|
||||||
|
info.is_transfer = (f.queueFlags & VK_QUEUE_TRANSFER_BIT) != 0;
|
||||||
|
info.is_present = (presentation_supported == VK_TRUE);
|
||||||
|
|
||||||
|
qs.available.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
qs.family_used.push_back(0);
|
||||||
|
|
||||||
|
family++;
|
||||||
|
}
|
||||||
|
u32 index = 0;
|
||||||
|
for (u32 i = 0; i < VULKAN_QUEUE_GRAPHICS_NUM; i++) {
|
||||||
|
for (const auto& idx : qs.available) {
|
||||||
|
if (idx.is_graphics) {
|
||||||
|
qs.family_used[qs.available.at(index).family]++;
|
||||||
|
qs.graphics.push_back(qs.available.at(index));
|
||||||
|
qs.available.erase(qs.available.begin() + index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index = 0;
|
||||||
|
for (u32 i = 0; i < VULKAN_QUEUE_COMPUTE_NUM; i++) {
|
||||||
|
for (const auto& idx : qs.available) {
|
||||||
|
if (idx.is_graphics) {
|
||||||
|
qs.family_used[qs.available.at(index).family]++;
|
||||||
|
qs.compute.push_back(qs.available.at(index));
|
||||||
|
qs.available.erase(qs.available.begin() + index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index = 0;
|
||||||
|
for (uint32_t i = 0; i < VULKAN_QUEUE_TRANSFER_NUM; i++) {
|
||||||
|
for (const auto& idx : qs.available) {
|
||||||
|
if (idx.is_graphics) {
|
||||||
|
qs.family_used[qs.available.at(index).family]++;
|
||||||
|
qs.transfer.push_back(qs.available.at(index));
|
||||||
|
qs.available.erase(qs.available.begin() + index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index = 0;
|
||||||
|
for (uint32_t i = 0; i < VULKAN_QUEUE_PRESENT_NUM; i++) {
|
||||||
|
for (const auto& idx : qs.available) {
|
||||||
|
if (idx.is_graphics) {
|
||||||
|
qs.family_used[qs.available.at(index).family]++;
|
||||||
|
qs.present.push_back(qs.available.at(index));
|
||||||
|
qs.available.erase(qs.available.begin() + index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return qs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Vulkan::vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
|
||||||
|
Emulator::VulkanSurfaceCapabilities* surfaceCap) {
|
||||||
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surfaceCap->capabilities);
|
||||||
|
|
||||||
|
uint32_t formats_count = 0;
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &formats_count, nullptr);
|
||||||
|
|
||||||
|
surfaceCap->formats = std::vector<VkSurfaceFormatKHR>(formats_count);
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &formats_count, surfaceCap->formats.data());
|
||||||
|
|
||||||
|
uint32_t present_modes_count = 0;
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_modes_count, nullptr);
|
||||||
|
|
||||||
|
surfaceCap->present_modes = std::vector<VkPresentModeKHR>(present_modes_count);
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_modes_count, surfaceCap->present_modes.data());
|
||||||
|
|
||||||
|
for (const auto& f : surfaceCap->formats) {
|
||||||
|
if (f.format == VK_FORMAT_B8G8R8A8_SRGB && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||||
|
surfaceCap->is_format_srgb_bgra32 = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (f.format == VK_FORMAT_B8G8R8A8_UNORM && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||||
|
surfaceCap->is_format_unorm_bgra32 = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_image_layout(VkCommandBuffer buffer, HLE::Libs::Graphics::VulkanImage* dst_image, uint32_t base_level, uint32_t levels,
|
||||||
|
VkImageAspectFlags aspect_mask, VkImageLayout old_image_layout, VkImageLayout new_image_layout) {
|
||||||
|
VkImageMemoryBarrier imageMemoryBarrier{};
|
||||||
|
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
imageMemoryBarrier.pNext = nullptr;
|
||||||
|
imageMemoryBarrier.srcAccessMask = 0;
|
||||||
|
imageMemoryBarrier.dstAccessMask = 0;
|
||||||
|
imageMemoryBarrier.oldLayout = old_image_layout;
|
||||||
|
imageMemoryBarrier.newLayout = new_image_layout;
|
||||||
|
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
imageMemoryBarrier.image = dst_image->image;
|
||||||
|
imageMemoryBarrier.subresourceRange.aspectMask = aspect_mask;
|
||||||
|
imageMemoryBarrier.subresourceRange.baseMipLevel = base_level;
|
||||||
|
imageMemoryBarrier.subresourceRange.levelCount = levels;
|
||||||
|
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
|
||||||
|
imageMemoryBarrier.subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
|
||||||
|
imageMemoryBarrier.srcAccessMask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
||||||
|
imageMemoryBarrier.dstAccessMask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
|
||||||
|
imageMemoryBarrier.dstAccessMask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
|
||||||
|
imageMemoryBarrier.srcAccessMask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_image_layout == VK_IMAGE_LAYOUT_PREINITIALIZED) {
|
||||||
|
imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
|
||||||
|
imageMemoryBarrier.srcAccessMask = 0;
|
||||||
|
imageMemoryBarrier.dstAccessMask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
|
||||||
|
imageMemoryBarrier.dstAccessMask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
|
||||||
|
imageMemoryBarrier.dstAccessMask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
|
||||||
|
vkCmdPipelineBarrier(buffer, src_stages, dest_stages, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
|
||||||
|
dst_image->layout = new_image_layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Vulkan::vulkanBlitImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanImage* src_image,
|
||||||
|
Emulator::VulkanSwapchain* dst_swapchain) {
|
||||||
|
auto* vk_buffer = buffer->getPool()->buffers[buffer->getIndex()];
|
||||||
|
|
||||||
|
HLE::Libs::Graphics::VulkanImage swapchain_image(HLE::Libs::Graphics::VulkanImageType::Unknown);
|
||||||
|
|
||||||
|
swapchain_image.image = dst_swapchain->swapchain_images[dst_swapchain->current_index];
|
||||||
|
swapchain_image.layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
|
||||||
|
set_image_layout(vk_buffer, src_image, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||||
|
set_image_layout(vk_buffer, &swapchain_image, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||||
|
|
||||||
|
VkImageBlit region{};
|
||||||
|
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
region.srcSubresource.mipLevel = 0;
|
||||||
|
region.srcSubresource.baseArrayLayer = 0;
|
||||||
|
region.srcSubresource.layerCount = 1;
|
||||||
|
region.srcOffsets[0].x = 0;
|
||||||
|
region.srcOffsets[0].y = 0;
|
||||||
|
region.srcOffsets[0].z = 0;
|
||||||
|
region.srcOffsets[1].x = static_cast<int>(src_image->extent.width);
|
||||||
|
region.srcOffsets[1].y = static_cast<int>(src_image->extent.height);
|
||||||
|
region.srcOffsets[1].z = 1;
|
||||||
|
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
region.dstSubresource.mipLevel = 0;
|
||||||
|
region.dstSubresource.baseArrayLayer = 0;
|
||||||
|
region.dstSubresource.layerCount = 1;
|
||||||
|
region.dstOffsets[0].x = 0;
|
||||||
|
region.dstOffsets[0].y = 0;
|
||||||
|
region.dstOffsets[0].z = 0;
|
||||||
|
region.dstOffsets[1].x = static_cast<int>(dst_swapchain->swapchain_extent.width);
|
||||||
|
region.dstOffsets[1].y = static_cast<int>(dst_swapchain->swapchain_extent.height);
|
||||||
|
region.dstOffsets[1].z = 1;
|
||||||
|
|
||||||
|
vkCmdBlitImage(vk_buffer, src_image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapchain_image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
||||||
|
®ion, VK_FILTER_LINEAR);
|
||||||
|
|
||||||
|
set_image_layout(vk_buffer, src_image, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Vulkan::vulkanFillImage(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanImage* dst_image, const void* src_data,
|
||||||
|
u64 size, u32 src_pitch, u64 dst_layout) {
|
||||||
|
HLE::Libs::Graphics::VulkanBuffer staging_buffer{};
|
||||||
|
staging_buffer.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||||
|
staging_buffer.memory.property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||||
|
vulkanCreateBuffer(ctx, size, &staging_buffer);
|
||||||
|
|
||||||
|
void* data = nullptr;
|
||||||
|
vkMapMemory(ctx->m_device, staging_buffer.memory.memory, staging_buffer.memory.offset, staging_buffer.memory.requirements.size, 0, &data);
|
||||||
|
std::memcpy(data, src_data, size);
|
||||||
|
vkUnmapMemory(ctx->m_device, staging_buffer.memory.memory);
|
||||||
|
|
||||||
|
GPU::CommandBuffer buffer(9);
|
||||||
|
|
||||||
|
buffer.begin();
|
||||||
|
vulkanBufferToImage(&buffer, &staging_buffer, src_pitch, dst_image, dst_layout);
|
||||||
|
buffer.end();
|
||||||
|
buffer.execute();
|
||||||
|
buffer.waitForFence();
|
||||||
|
|
||||||
|
vulkanDeleteBuffer(ctx, &staging_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Vulkan::vulkanBufferToImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanBuffer* src_buffer, u32 src_pitch,
|
||||||
|
HLE::Libs::Graphics::VulkanImage* dst_image, u64 dst_layout) {
|
||||||
|
auto* vk_buffer = buffer->getPool()->buffers[buffer->getIndex()];
|
||||||
|
|
||||||
|
set_image_layout(vk_buffer, dst_image, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||||
|
|
||||||
|
VkBufferImageCopy region{};
|
||||||
|
region.bufferOffset = 0;
|
||||||
|
region.bufferRowLength = (src_pitch != dst_image->extent.width ? src_pitch : 0);
|
||||||
|
region.bufferImageHeight = 0;
|
||||||
|
|
||||||
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
region.imageSubresource.mipLevel = 0;
|
||||||
|
region.imageSubresource.baseArrayLayer = 0;
|
||||||
|
region.imageSubresource.layerCount = 1;
|
||||||
|
|
||||||
|
region.imageOffset = {0, 0, 0};
|
||||||
|
region.imageExtent = {dst_image->extent.width, dst_image->extent.height, 1};
|
||||||
|
|
||||||
|
vkCmdCopyBufferToImage(vk_buffer, src_buffer->buffer, dst_image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||||
|
|
||||||
|
set_image_layout(vk_buffer, dst_image, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
static_cast<VkImageLayout>(dst_layout));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Vulkan::vulkanCreateBuffer(HLE::Libs::Graphics::GraphicCtx* ctx, u64 size, HLE::Libs::Graphics::VulkanBuffer* buffer) {
|
||||||
|
VkBufferCreateInfo buffer_info{};
|
||||||
|
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
|
buffer_info.size = size;
|
||||||
|
buffer_info.usage = buffer->usage;
|
||||||
|
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
|
||||||
|
vkCreateBuffer(ctx->m_device, &buffer_info, nullptr, &buffer->buffer);
|
||||||
|
|
||||||
|
vkGetBufferMemoryRequirements(ctx->m_device, buffer->buffer, &buffer->memory.requirements);
|
||||||
|
|
||||||
|
bool allocated = GPU::vulkanAllocateMemory(ctx, &buffer->memory);
|
||||||
|
if (!allocated) {
|
||||||
|
printf("Can't allocate vulkan\n");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
vkBindBufferMemory(ctx->m_device, buffer->buffer, buffer->memory.memory, buffer->memory.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Vulkan::vulkanDeleteBuffer(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanBuffer* buffer) {
|
||||||
|
vkDestroyBuffer(ctx->m_device, buffer->buffer, nullptr);
|
||||||
|
vkFreeMemory(ctx->m_device, buffer->memory.memory, nullptr);
|
||||||
|
buffer->memory.memory = nullptr;
|
||||||
|
buffer->buffer = nullptr;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
#include <Core/PS4/HLE/Graphics/graphics_render.h>
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <src/video/khronos/vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "emulator.h"
|
||||||
|
|
||||||
|
namespace Graphics::Vulkan {
|
||||||
|
|
||||||
|
constexpr int VULKAN_QUEUES_NUM = 11; // Total of the above
|
||||||
|
constexpr int VULKAN_QUEUE_GRAPHICS_NUM = 1;
|
||||||
|
constexpr int VULKAN_QUEUE_TRANSFER_NUM = 1;
|
||||||
|
constexpr int VULKAN_QUEUE_PRESENT_NUM = 1;
|
||||||
|
constexpr int VULKAN_QUEUE_COMPUTE_NUM = 8;
|
||||||
|
|
||||||
|
constexpr int VULKAN_QUEUE_GFX = 8;
|
||||||
|
constexpr int VULKAN_QUEUE_UTIL = 9;
|
||||||
|
constexpr int VULKAN_QUEUE_PRESENT = 10;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& clamp(const T& x, const T& min, const T& max) {
|
||||||
|
if (x < min) return min;
|
||||||
|
if (x > max) return max;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vulkanCreate(Emulator::WindowCtx* ctx);
|
||||||
|
void vulkanGetInstanceExtensions(Emulator::VulkanExt* ext);
|
||||||
|
void vulkanFindCompatiblePhysicalDevice(VkInstance instance, VkSurfaceKHR surface, const std::vector<const char*>& device_extensions,
|
||||||
|
Emulator::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
||||||
|
Emulator::VulkanQueues* out_queues);
|
||||||
|
VkDevice vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emulator::VulkanExt* r,
|
||||||
|
const Emulator::VulkanQueues& queues, const std::vector<const char*>& device_extensions);
|
||||||
|
Emulator::VulkanQueues vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||||
|
void vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface, Emulator::VulkanSurfaceCapabilities* surfaceCap);
|
||||||
|
void vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emulator::VulkanQueues& queues);
|
||||||
|
Emulator::VulkanSwapchain* vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count);
|
||||||
|
void vulkanBlitImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanImage* src_image, Emulator::VulkanSwapchain* dst_swapchain);
|
||||||
|
void vulkanFillImage(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanImage* dst_image, const void* src_data, u64 size, u32 src_pitch,
|
||||||
|
u64 dst_layout);
|
||||||
|
void vulkanBufferToImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanBuffer* src_buffer, u32 src_pitch,
|
||||||
|
HLE::Libs::Graphics::VulkanImage* dst_image, u64 dst_layout);
|
||||||
|
void vulkanCreateBuffer(HLE::Libs::Graphics::GraphicCtx* ctx, u64 size, HLE::Libs::Graphics::VulkanBuffer* buffer);
|
||||||
|
void vulkanDeleteBuffer(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanBuffer* buffer);
|
||||||
|
}; // namespace Graphics::Vulkan
|
|
@ -33,6 +33,8 @@ add_subdirectory(winpthread)
|
||||||
add_subdirectory(discord-rpc)
|
add_subdirectory(discord-rpc)
|
||||||
#=================== toml11 ===================
|
#=================== toml11 ===================
|
||||||
add_subdirectory(toml11)
|
add_subdirectory(toml11)
|
||||||
|
#=================== vulkan ==================
|
||||||
|
add_subdirectory(vulkan)
|
||||||
#=================== IMGUI ===================
|
#=================== IMGUI ===================
|
||||||
|
|
||||||
set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/imgui)
|
set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/imgui)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 8c9feb4f480b32f7c7421af546aa6ffb558bdd5e
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 058e54b10b10c658fd50862e6f65f55522afa182
|
Loading…
Reference in New Issue