Skip to content
Snippets Groups Projects
Commit e7cd38e5 authored by Geo Ster's avatar Geo Ster
Browse files

VULKAN: Add texture support

* This allows sampling of textures from the fragment shader. Storage buffers are the only thing left now to implement
parent ec9638f5
No related branches found
No related tags found
No related merge requests found
......@@ -40,6 +40,7 @@ set(SOURCES
src/gs/vulkan/context.cc
src/gs/vulkan/window.cc
src/gs/vulkan/buffer.cc
src/gs/vulkan/texture.cc
src/cpu/ee/jit/jit.cc
src/cpu/ee/jit/ir.cc
)
......@@ -69,9 +70,11 @@ set(HEADERS
src/media/gamepad.h
src/gs/gsvram.h
src/gs/gsrenderer.h
src/gs/vulkan/common.h
src/gs/vulkan/context.h
src/gs/vulkan/window.h
src/gs/vulkan/buffer.h
src/gs/vulkan/texture.h
src/cpu/ee/jit/jit.h
src/cpu/ee/jit/ir.h
)
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 6.0.2, 2022-03-17T12:34:32. -->
<!-- Written by QtCreator 7.0.0, 2022-03-29T16:59:09. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
......@@ -92,12 +92,12 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{087969e2-05f2-4e4e-9c78-c50218a2d507}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="CMake.Build.Type">Debug</value>
<value type="QString" key="CMake.Initial.Parameters">-GUnix Makefiles
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Unix Makefiles
-DCMAKE_BUILD_TYPE:STRING=Debug
-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake
-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
......@@ -143,7 +143,7 @@
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="CMake.Build.Type">Release</value>
<value type="QString" key="CMake.Initial.Parameters">-GUnix Makefiles
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Unix Makefiles
-DCMAKE_BUILD_TYPE:STRING=Release
-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake
-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
......@@ -189,7 +189,7 @@
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
<value type="QString" key="CMake.Initial.Parameters">-GUnix Makefiles
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Unix Makefiles
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake
-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
......@@ -235,7 +235,7 @@
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.3">
<value type="QString" key="CMake.Build.Type">MinSizeRel</value>
<value type="QString" key="CMake.Initial.Parameters">-GUnix Makefiles
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Unix Makefiles
-DCMAKE_BUILD_TYPE:STRING=MinSizeRel
-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake
-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
......@@ -308,7 +308,7 @@
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/emufan/Desktop/build-ps2_emu-Desktop-Release/bin</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/emufan/Desktop/build-ps2_emu-Desktop-Debug/bin</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
......
......@@ -242,7 +242,7 @@ namespace gs
break;
case 0x4c:
case 0x4d:
frame[context] = data;
frame[context].value = data;
break;
case 0x4e:
case 0x4f:
......@@ -338,9 +338,17 @@ namespace gs
/* Check if transfer has completed */
if (data_written >= trxreg.width * trxreg.height)
{
// Reset counters
fmt::print("[GS] HWREG transfer complete!\n");
data_written = 0;
// Copy texture data to the GPU
auto ptr = reinterpret_cast<uint8_t*>(&vram[frame[0].base_ptr * 32]);
std::vector<uint8_t> pixels(640 * 368 * 4);
pixels.assign(ptr, ptr + pixels.size());
renderer.vram->copy_pixels(pixels);
/* Deactivate TRXDIR */
trxdir = TRXDir::None;
}
......
......@@ -161,6 +161,21 @@ namespace gs
None = 3
};
union FRAME
{
uint64_t value;
struct
{
uint64_t base_ptr : 9;
uint64_t : 7;
uint64_t width : 6;
uint64_t : 2;
uint64_t format : 6;
uint64_t : 2;
uint64_t drawing_mask : 32;
};
};
struct GraphicsSynthesizer : public common::Component
{
friend struct GIF;
......@@ -206,7 +221,8 @@ namespace gs
uint64_t dimx = 0, dthe = 0, colclamp = 0;
uint64_t test[2] = {};
uint64_t pabe = 0, fba[2];
uint64_t frame[2] = {}, zbuf[2] = {};
uint64_t zbuf[2] = {};
FRAME frame[2] = {};
BITBLTBUF bitbltbuf = {};
TRXPOS trxpos = {};
TRXREG trxreg = {};
......
......@@ -19,27 +19,29 @@ namespace gs
auto fragment = context->create_shader_module("shaders/fragment.glsl.spv");
fragment.stage = vk::ShaderStageFlagBits::eFragment;
// Create VRAM texture and vertex buffer
vram = std::make_unique<VkTexture>(context);
buffer = std::make_unique<Buffer>(context, MAX_VERTICES);
// Configure texture
vram->create(640 * 200, 1, vk::ImageType::e1D, vk::Format::eR32Uint);
// Construct graphics pipeline
context->create_descriptor_sets(*vram);
context->create_graphics_pipeline(vertex, fragment);
buffer = std::make_unique<Buffer>(context, MAX_VERTICES);
}
GSRenderer::~GSRenderer()
{
// Manually destroy used vulkan resources
buffer->destroy();
vram->destroy();
}
void GSRenderer::set_depth_function(uint32_t test_bits)
{
auto& context = window->context;
//auto& device = context->device;
//auto& queue = context->graphics_queue;
// Create an one-time submit command buffer
//vk::CommandBufferAllocateInfo alloc_info(context->command_pool, vk::CommandBufferLevel::ePrimary, 1);
//vk::CommandBuffer command_buffer = device->allocateCommandBuffers(alloc_info)[0];
//command_buffer.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
// Set depth function
auto& command_buffer = context->get_command_buffer();
......@@ -57,15 +59,6 @@ namespace gs
default:
common::Emulator::terminate("[GS] Unknown depth function selected!\n");
}
//command_buffer.end();
//vk::SubmitInfo submit_info({}, {}, {}, 1, &command_buffer);
//queue.submit(submit_info, nullptr);
//queue.waitIdle();
//device->freeCommandBuffers(context->command_pool, command_buffer);
}
void GSRenderer::render()
......@@ -77,6 +70,8 @@ namespace gs
auto& command_buffer = window->context->get_command_buffer();
vk::DeviceSize offsets[1] = { 0 };
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, window->context->pipeline_layout, 0,
window->context->descriptor_sets[window->current_frame], {});
command_buffer.bindVertexBuffers(0, 1, &buffer->local_buffer, offsets);
command_buffer.draw(vertex_count, 1, draw_data.size() - vertex_count, 0);
vertex_count = 0;
......
#pragma once
#include <gs/vulkan/buffer.h>
#include <gs/vulkan/texture.h>
#include <cstdint>
#include <memory>
#include <vector>
......@@ -39,6 +40,7 @@ namespace gs
VkWindow* window = nullptr;
std::vector<GSVertex> draw_data;
std::unique_ptr<Buffer> buffer;
std::unique_ptr<VkTexture> vram;
int vertex_count = 0;
};
}
......@@ -71,7 +71,6 @@ void Buffer::copy_buffer(vk::Buffer src_buffer, vk::Buffer dst_buffer, vk::Devic
vk::BufferCopy copy_region(0, 0, size);
command_buffer.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
//auto& command_buffer = context->get_command_buffer();
command_buffer.copyBuffer(src_buffer, dst_buffer, copy_region);
command_buffer.end();
......
......@@ -11,21 +11,24 @@ class VkContext;
struct VertexInfo
{
VertexInfo() = default;
VertexInfo(glm::vec3 position, glm::vec3 color) : position(position), color(color) {};
VertexInfo(glm::vec3 position, glm::vec3 color, glm::vec2 coords) :
position(position), color(color), texcoords(coords) {};
glm::vec3 position;
glm::vec3 color;
glm::vec2 texcoords;
};
struct Vertex : public VertexInfo
{
Vertex() = default;
Vertex(glm::vec3 position, glm::vec3 color = {}) : VertexInfo(position, color) {};
Vertex(glm::vec3 position, glm::vec3 color = {}, glm::vec2 coords = {}) : VertexInfo(position, color, coords) {};
static constexpr auto binding_desc = vk::VertexInputBindingDescription(0, sizeof(VertexInfo));
static constexpr std::array<vk::VertexInputAttributeDescription, 2> attribute_desc =
static constexpr std::array<vk::VertexInputAttributeDescription, 3> attribute_desc =
{
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(VertexInfo, position)),
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(VertexInfo, color)),
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(VertexInfo, texcoords)),
};
};
......
#pragma once
struct NonCopyable
{
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
#include <gs/vulkan/context.h>
#include <gs/vulkan/buffer.h>
#include <gs/vulkan/window.h>
#include <gs/vulkan/texture.h>
#include <fstream>
#include <array>
......@@ -15,6 +16,8 @@ VkContext::~VkContext()
{
device->waitIdle();
device->destroyDescriptorSetLayout(descriptor_layout);
device->destroyDescriptorPool(descriptor_pool);
device->freeCommandBuffers(command_pool, command_buffers);
device->destroyPipelineLayout(pipeline_layout);
device->destroyPipeline(graphics_pipeline);
......@@ -58,10 +61,7 @@ void VkContext::create_devices(int device_id)
{
// Pick a physical device
auto physical_devices = instance->enumeratePhysicalDevices();
if (!physical_devices.empty())
physical_device = physical_devices[device_id];
else
throw std::runtime_error("[VK] Failed to find GPU with Vulkan support!");
physical_device = physical_devices.front();
// Get available queue family properties
auto family_props = physical_device.getQueueFamilyProperties();
......@@ -82,7 +82,11 @@ void VkContext::create_devices(int device_id)
std::array<const char*, 1> device_extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
auto queue_info = vk::DeviceQueueCreateInfo({}, queue_family, 1, &default_queue_priority);
vk::DeviceCreateInfo device_info({}, 1, &queue_info, 0, nullptr, device_extensions.size(), device_extensions.data());
std::array<vk::PhysicalDeviceFeatures, 1> features = {};
features[0].samplerAnisotropy = true;
vk::DeviceCreateInfo device_info({}, 1, &queue_info, 0, nullptr, device_extensions.size(), device_extensions.data(), features.data());
device = physical_device.createDeviceUnique(device_info);
graphics_queue = device->getQueue(queue_family, 0);
......@@ -198,11 +202,11 @@ void VkContext::create_graphics_pipeline(VkShader& vertex, VkShader& fragment)
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
vk::PipelineColorBlendStateCreateInfo color_blending({}, VK_FALSE, vk::LogicOp::eCopy, 1, &colorblend_attachment, {0});
vk::PipelineLayoutCreateInfo pipeline_layout_info({}, 0, nullptr, 0, nullptr);
vk::PipelineLayoutCreateInfo pipeline_layout_info({}, 1, &descriptor_layout, 0, nullptr);
try {
pipeline_layout = device->createPipelineLayout(pipeline_layout_info);
} catch (vk::SystemError err) {
throw std::runtime_error("failed to create pipeline layout!");
throw std::runtime_error("[VK] Failed to create pipeline layout!");
}
vk::DynamicState dynamic_states[2] = { vk::DynamicState::eDepthCompareOp, vk::DynamicState::eLineWidth };
......@@ -210,7 +214,7 @@ void VkContext::create_graphics_pipeline(VkShader& vertex, VkShader& fragment)
// Depth and stencil state containing depth and stencil compare and test operations
// We only use depth tests and want depth tests and writes to be enabled and compare with less or equal
vk::PipelineDepthStencilStateCreateInfo depth_info({}, VK_TRUE, VK_TRUE, vk::CompareOp::eAlways, VK_FALSE, VK_FALSE);
vk::PipelineDepthStencilStateCreateInfo depth_info({}, VK_TRUE, VK_TRUE, vk::CompareOp::eGreaterOrEqual, VK_FALSE, VK_TRUE);
depth_info.back.failOp = vk::StencilOp::eKeep;
depth_info.back.passOp = vk::StencilOp::eKeep;
depth_info.back.compareOp = vk::CompareOp::eAlways;
......@@ -240,6 +244,40 @@ void VkContext::create_graphics_pipeline(VkShader& vertex, VkShader& fragment)
throw std::runtime_error("[VK] Couldn't create graphics pipeline");
}
constexpr int MAX_FRAMES_IN_FLIGHT = 3;
void VkContext::create_descriptor_sets(VkTexture& texture)
{
vk::DescriptorSetLayoutBinding sampler_layout_binding(0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment);
std::array<vk::DescriptorSetLayoutBinding, 1> bindings = { sampler_layout_binding };
vk::DescriptorSetLayoutCreateInfo layout_info({}, bindings.size(), bindings.data());
descriptor_layout = device->createDescriptorSetLayout(layout_info);
std::array<vk::DescriptorPoolSize, 1> pool_sizes = { vk::DescriptorPoolSize(vk::DescriptorType::eCombinedImageSampler, MAX_FRAMES_IN_FLIGHT) };
vk::DescriptorPoolCreateInfo pool_info({}, MAX_FRAMES_IN_FLIGHT, pool_sizes.size(), pool_sizes.data());
descriptor_pool = device->createDescriptorPool(pool_info);
std::vector<vk::DescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, descriptor_layout);
vk::DescriptorSetAllocateInfo alloc_info(descriptor_pool, MAX_FRAMES_IN_FLIGHT, layouts.data());
descriptor_sets = device->allocateDescriptorSets(alloc_info);
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
auto& set = descriptor_sets[i];
vk::DescriptorImageInfo image_info(texture.texture_sampler, texture.texture_view, vk::ImageLayout::eShaderReadOnlyOptimal);
std::array<vk::WriteDescriptorSet, 1> descriptor_writes =
{
vk::WriteDescriptorSet(set, 0, 0, 1, vk::DescriptorType::eCombinedImageSampler, &image_info)
};
device->updateDescriptorSets(descriptor_writes, {});
}
}
void VkContext::create_command_pool()
{
vk::CommandPoolCreateInfo pool_info(vk::CommandPoolCreateFlagBits::eResetCommandBuffer, queue_family);
......
......@@ -32,6 +32,8 @@ struct PipelineLayoutInfo
std::vector<vk::PushConstantRange> push_const_ranges;
};
class VkTexture;
// The vulkan context. Can only be created by the window
class VkContext
{
......@@ -45,6 +47,7 @@ public:
VkShader create_shader_module(std::filesystem::path filepath);
vk::UniquePipelineLayout create_pipeline_layout(const PipelineLayoutInfo& info) const;
void create_graphics_pipeline(VkShader& vertex, VkShader& fragment);
void create_descriptor_sets(VkTexture& texture);
void init();
void destroy();
......@@ -71,6 +74,9 @@ public:
vk::PipelineLayout pipeline_layout;
vk::Pipeline graphics_pipeline;
vk::RenderPass renderpass;
vk::DescriptorSetLayout descriptor_layout;
vk::DescriptorPool descriptor_pool;
std::vector<vk::DescriptorSet> descriptor_sets;
// Command buffer
vk::CommandPool command_pool;
......
#include <gs/vulkan/texture.h>
#include <gs/vulkan/buffer.h>
#include <gs/vulkan/context.h>
VkTexture::VkTexture(std::shared_ptr<VkContext> context) :
context(context)
{
}
VkTexture::~VkTexture()
{
}
void VkTexture::create(int width, int height, vk::ImageType type, vk::Format format)
{
this->format = format;
this->width = width;
this->height = height;
auto& device = context->device;
int image_size = 0;
switch (format)
{
case vk::Format::eR8G8B8A8Uint:
case vk::Format::eR8G8B8A8Srgb:
case vk::Format::eR32Uint:
image_size = width * height * 4;
break;
case vk::Format::eR8G8B8Uint:
image_size = width * height * 3;
break;
default:
throw std::runtime_error("[VK] Unknown texture format");
}
create_buffer(image_size, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent, staging, staging_memory);
pixels = device->mapMemory(staging_memory, 0, image_size);
// Create the texture
vk::ImageCreateInfo image_info
(
{},
type,
format,
{ width, height, 1 }, 1, 1,
vk::SampleCountFlagBits::e1,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled
);
texture = device->createImage(image_info);
// Create texture memory
auto requirements = device->getImageMemoryRequirements(texture);
auto memory_index = Buffer::find_memory_type(requirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal, context);
vk::MemoryAllocateInfo alloc_info(requirements.size, memory_index);
texture_memory = device->allocateMemory(alloc_info);
device->bindImageMemory(texture, texture_memory, 0);
// Create texture view
vk::ImageViewCreateInfo view_info({}, texture, vk::ImageViewType::e1D, format, {},
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
texture_view = device->createImageView(view_info);
// Create texture sampler
auto props = context->physical_device.getProperties();
vk::SamplerCreateInfo sampler_info({}, vk::Filter::eNearest, vk::Filter::eNearest, vk::SamplerMipmapMode::eNearest, vk::SamplerAddressMode::eClampToEdge,
vk::SamplerAddressMode::eClampToEdge, vk::SamplerAddressMode::eClampToEdge, {}, true, props.limits.maxSamplerAnisotropy,
false, vk::CompareOp::eAlways, {}, {}, vk::BorderColor::eIntOpaqueBlack, false);
texture_sampler = device->createSampler(sampler_info);
}
void VkTexture::destroy()
{
auto& device = context->device;
device->waitIdle();
device->destroyImageView(texture_view);
device->destroyImage(texture);
device->freeMemory(texture_memory);
device->unmapMemory(staging_memory);
device->freeMemory(staging_memory);
device->destroySampler(texture_sampler);
device->destroyBuffer(staging);
}
void VkTexture::transition_layout(vk::ImageLayout old_layout, vk::ImageLayout new_layout)
{
auto& device = context->device;
auto& queue = context->graphics_queue;
vk::CommandBufferAllocateInfo alloc_info(context->command_pool, vk::CommandBufferLevel::ePrimary, 1);
vk::CommandBuffer command_buffer = device->allocateCommandBuffers(alloc_info)[0];
command_buffer.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
vk::ImageMemoryBarrier barrier({}, {}, old_layout, new_layout, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, texture,
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
std::array<vk::ImageMemoryBarrier, 1> barriers = { barrier };
vk::PipelineStageFlags source_stage, destination_stage;
if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eTransferDstOptimal)
{
barrier.srcAccessMask = vk::AccessFlagBits::eNone;
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
source_stage = vk::PipelineStageFlagBits::eTopOfPipe;
destination_stage = vk::PipelineStageFlagBits::eTransfer;
}
else if (old_layout == vk::ImageLayout::eTransferDstOptimal && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal)
{
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
source_stage = vk::PipelineStageFlagBits::eTransfer;
destination_stage = vk::PipelineStageFlagBits::eFragmentShader;
}
else
{
throw std::invalid_argument("[VK] Unsupported layout transition!");
}
command_buffer.pipelineBarrier(source_stage, destination_stage, vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
command_buffer.end();
vk::SubmitInfo submit_info({}, {}, {}, 1, &command_buffer);
queue.submit(submit_info, nullptr);
queue.waitIdle();
device->freeCommandBuffers(context->command_pool, command_buffer);
}
void VkTexture::copy_pixels(const std::vector<uint8_t>& new_pixels)
{
auto& device = context->device;
auto& queue = context->graphics_queue;
// Transition image to transfer format
transition_layout(vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
// Copy pixels to staging buffer
std::memcpy(pixels, new_pixels.data(), new_pixels.size() * sizeof(new_pixels[0]));
// Copy the staging buffer to the image
vk::CommandBufferAllocateInfo alloc_info(context->command_pool, vk::CommandBufferLevel::ePrimary, 1);
vk::CommandBuffer command_buffer = device->allocateCommandBuffers(alloc_info)[0];
command_buffer.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
vk::BufferImageCopy region(0, 0, 0, vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), {0}, {width,height,1});
std::array<vk::BufferImageCopy, 1> regions = { region };
command_buffer.copyBufferToImage(staging, texture, vk::ImageLayout::eTransferDstOptimal, regions);
command_buffer.end();
vk::SubmitInfo submit_info({}, {}, {}, 1, &command_buffer);
queue.submit(submit_info, nullptr);
queue.waitIdle();
device->freeCommandBuffers(context->command_pool, command_buffer);
// Prepare for shader reads
transition_layout(vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
}
void VkTexture::create_buffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties,
vk::Buffer& buffer, vk::DeviceMemory& buffer_memory)
{
auto& device = context->device;
vk::BufferCreateInfo bufferInfo({}, size, usage);
buffer = device->createBuffer(bufferInfo);
vk::MemoryRequirements mem_requirements = device->getBufferMemoryRequirements(buffer);
auto memory_type_index = Buffer::find_memory_type(mem_requirements.memoryTypeBits, properties, context);
vk::MemoryAllocateInfo alloc_info(mem_requirements.size, memory_type_index);
buffer_memory = device->allocateMemory(alloc_info);
device->bindBufferMemory(buffer, buffer_memory, 0);
}
#pragma once
#include <vulkan/vulkan.hpp>
#include <memory>
class VkContext;
class VkTexture
{
public:
VkTexture(std::shared_ptr<VkContext> context);
~VkTexture();
void create(int width, int height, vk::ImageType type, vk::Format format = vk::Format::eR8G8B8A8Uint);
void copy_pixels(const std::vector<uint8_t>& pixels);
void destroy();
private:
void transition_layout(vk::ImageLayout old_layout, vk::ImageLayout new_layout);
void create_buffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties,
vk::Buffer& buffer, vk::DeviceMemory& buffer_memory);
public:
vk::Buffer staging;
vk::DeviceMemory staging_memory;
uint32_t width, height;
void* pixels;
// Texture objects
vk::Image texture;
vk::ImageView texture_view;
vk::DeviceMemory texture_memory;
vk::Sampler texture_sampler;
vk::Format format;
std::shared_ptr<VkContext> context;
};
......@@ -36,9 +36,6 @@ void VkWindow::destroy()
auto& device = context->device;
device->waitIdle();
buffers.clear();
device->destroySwapchainKHR(swapchain);
// Destroy sync objects
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
......@@ -52,6 +49,9 @@ void VkWindow::destroy()
device->destroyImageView(depth_buffer.view);
device->freeMemory(depth_buffer.memory);
buffers.clear();
device->destroySwapchainKHR(swapchain);
context->instance->destroySurfaceKHR(surface);
}
......
......@@ -11,7 +11,6 @@ struct SwapchainBuffer
~SwapchainBuffer()
{
device->destroyImageView(view);
device->destroyImage(image);
device->destroyFramebuffer(framebuffer);
}
......
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <common/emulator.h>
#include <stdexcept>
#include <gs/vulkan/window.h>
#include <gs/vulkan/context.h>
#include <gs/vulkan/buffer.h>
int main()
{
VkWindow window(800, 600, "PS2");
common::Emulator* emulator = new common::Emulator(&window);
common::Emulator emulator(&window);
try
{
while (!window.should_close())
{
window.begin_frame();
emulator->tick();
emulator.tick();
window.end_frame();
}
......@@ -27,6 +24,6 @@ int main()
}
window.destroy();
delete emulator;
return 0;
}
......@@ -3,8 +3,53 @@
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
layout(location = 1) in vec2 fragTexCoord;
layout(binding = 0) uniform usampler1D texSampler;
uint convert_to_psmct32(ivec2 coord)
{
/* Get offset from texture */
int dest_base = coord.y * 640 + coord.x;
/* Get page offset */
int page_x = coord.x / 64;
int page_y = coord.y / 32;
int page = page_y * 10 + page_x;
/* Block layout */
const int block_layout[4][8] = int[4][8](
int[8](0, 1, 4, 5, 16, 17, 20, 21),
int[8](2, 3, 6, 7, 18, 19, 22, 23),
int[8](8, 9, 12, 13, 24, 25, 28, 29),
int[8](10, 11, 14, 15, 26, 27, 30, 31));
int block_x = (coord.x / 8) % 8;
int block_y = (coord.y / 8) % 4;
int block = block_layout[block_y][block_x];
/* Pixel layout */
const int pixels[2][8] = int[2][8](int[8](0, 1, 4, 5, 8, 9, 12, 13),
int[8](2, 3, 6, 7, 10, 11, 14, 15));
int column = (coord.y / 2) % 4;
int pixel = pixels[coord.y & 1][coord.x % 8];
int offset = column * 16 + pixel;
int final_offset = page * 2048 + block * 64 + offset;
return final_offset;
}
void main()
{
outColor = vec4(fragColor, 1.0);
vec2 screen_coords = gl_FragCoord.xy * (vec2(640, 368) / vec2(800, 600));
// Account for VRAM swizzling
uint swizzled_coords = convert_to_psmct32(ivec2(gl_FragCoord));
uint pixel = texelFetch(texSampler, int(swizzled_coords), 0).r;
uvec3 rgb = uvec3(pixel & 0xff, (pixel >> 8) & 0xff, (pixel >> 16) & 0xff);
vec3 color = vec3(rgb) / vec3(255.0f);
color += fragColor;
outColor = vec4(color, 1.0);
}
......@@ -3,11 +3,14 @@
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;
void main()
{
gl_Position = vec4(inPosition, 1.0);
fragColor = inColor;
fragTexCoord = inTexCoord;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment