RenderTextures and Rml Geometry

This commit is contained in:
legatt 2026-05-12 14:48:56 +03:00
parent eb79ff9879
commit 68ec759c36
9 changed files with 252 additions and 21 deletions

View File

@ -6,6 +6,7 @@ set(LIB vulkan-renderer)
find_package(Vulkan REQUIRED) find_package(Vulkan REQUIRED)
find_package(glm CONFIG REQUIRED) find_package(glm CONFIG REQUIRED)
find_package(RmlUi CONFIG REQUIRED)
# Link library # Link library
@ -26,4 +27,5 @@ target_link_libraries(${LIB} PUBLIC utils)
target_link_libraries(${LIB} PUBLIC p7) target_link_libraries(${LIB} PUBLIC p7)
target_link_libraries(${LIB} PRIVATE glm::glm) target_link_libraries(${LIB} PRIVATE glm::glm)
target_link_libraries(${LIB} PRIVATE Vulkan::Vulkan) target_link_libraries(${LIB} PRIVATE Vulkan::Vulkan)
target_link_libraries(${LIB} PRIVATE RmlUi::RmlUi)

View File

@ -18,7 +18,7 @@ namespace ee_vulkan {
static constexpr vk::ClearValue CLEAR_DEPTH = {.depthStencil = {1.0f, 0}}; static constexpr vk::ClearValue CLEAR_DEPTH = {.depthStencil = {1.0f, 0}};
}; };
struct Mesh { struct ProvinceMesh {
lazy<vulkan_render::Buffer< lazy<vulkan_render::Buffer<
glm::vec2, glm::vec2,
vk::BufferUsageFlagBits::eVertexBuffer, vk::BufferUsageFlagBits::eVertexBuffer,
@ -76,7 +76,7 @@ export namespace ee_vulkan {
lazy<vulkan_render::Texture<vulkan_render::TextureMode::DEPTH>> depth; lazy<vulkan_render::Texture<vulkan_render::TextureMode::DEPTH>> depth;
lazy<vulkan_render::Uniform<glm::mat4>> viewProjectionUniform; lazy<vulkan_render::Uniform<glm::mat4>> viewProjectionUniform;
Mesh provinces; ProvinceMesh provinces;
std::size_t currentFrame = 0; std::size_t currentFrame = 0;

View File

@ -47,6 +47,15 @@ export namespace vulkan_render {
RenderCommands(const Context &context, std::uint32_t maxFramesInFlight); RenderCommands(const Context &context, std::uint32_t maxFramesInFlight);
void SubmitStagingBuffer(const BaseBuffer<BufferMode::STAGING_ACCESS> &buffer) const; void SubmitStagingBuffer(const BaseBuffer<BufferMode::STAGING_ACCESS> &buffer) const;
void SubmitTexture(
const BaseBuffer<BufferMode::STATIC> &buffer,
const Texture<TextureMode::PARTIAL> &texture
) const;
void TranslateTextureLayout(
const Texture<TextureMode::PARTIAL> &texture,
const vk::ImageLayout &from,
const vk::ImageLayout &to
) const;
private: private:
static vk::raii::CommandPool MakePool(const Context &context, std::uint32_t queueFamily); static vk::raii::CommandPool MakePool(const Context &context, std::uint32_t queueFamily);

View File

@ -149,6 +149,7 @@ export namespace vulkan_render {
}; };
enum class TextureMode { enum class TextureMode {
PARTIAL,
RENDER, RENDER,
DEPTH DEPTH
}; };
@ -171,35 +172,25 @@ export namespace vulkan_render {
Texture( Texture(
const Context &context, const Context &context,
const std::uint32_t width, const std::uint32_t height, const std::uint32_t width, const std::uint32_t height,
const vk::Format &format, const vk::Format &format
const std::span<std::byte> textureData ) requires (mode == TextureMode::PARTIAL)
) requires (mode == TextureMode::RENDER) : context(context), width(width), height(height), format(format) {
: context(context), width(width), height(height), data(textureData), format(format) {
Buffer<
std::byte,
vk::BufferUsageFlagBits::eTransferSrc,
BufferMode::STATIC
> stagingBuffer(context, data);
MakeInstance( MakeInstance(
vk::Flags(vk::ImageUsageFlagBits::eTransferDst) | vk::ImageUsageFlagBits::eSampled, vk::Flags(vk::ImageUsageFlagBits::eTransferDst) | vk::ImageUsageFlagBits::eSampled,
vk::MemoryPropertyFlagBits::eDeviceLocal, vk::MemoryPropertyFlagBits::eDeviceLocal,
handle, handle,
memory memory
); );
/* TODO: Transition image layout
* Copy buffer to image
* Transition image layout
* Create view
*/
} }
Texture( Texture(
const Context &context, const Context &context,
const Surface &surface const Surface &surface
) requires (mode == TextureMode::DEPTH) ) requires (mode == TextureMode::DEPTH) :
: context(context), width(surface.extent.width), height(surface.extent.height), format(vk::Format::eD32SfloatS8Uint) { context(context),
width(surface.extent.width),
height(surface.extent.height),
format(vk::Format::eD32SfloatS8Uint) {
MakeInstance( MakeInstance(
vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::ImageUsageFlagBits::eDepthStencilAttachment,
vk::MemoryPropertyFlagBits::eDeviceLocal, vk::MemoryPropertyFlagBits::eDeviceLocal,
@ -209,6 +200,21 @@ export namespace vulkan_render {
MakeView(vk::ImageAspectFlagBits::eDepth); MakeView(vk::ImageAspectFlagBits::eDepth);
} }
// ReSharper disable once CppNonExplicitConvertingConstructor
Texture(Texture<TextureMode::PARTIAL> &&from) requires (mode == TextureMode::RENDER) :
context(from.context),
width(from.width),
height(from.height),
format(from.format) {
if (*from.view == nullptr) {
LOG(ERROR, "Corrupted render texture created, source data is not complete.");
}
}
void Init(const vk::ImageAspectFlags &flags) requires (mode == TextureMode::PARTIAL) {
MakeView(flags);
}
Texture(Texture&&) noexcept = default; Texture(Texture&&) noexcept = default;
Texture(const Texture&) = delete; Texture(const Texture&) = delete;
Texture& operator=(const Texture&) = delete; Texture& operator=(const Texture&) = delete;

View File

@ -5,6 +5,8 @@
export module vulkan_render:factory; export module vulkan_render:factory;
import :hardware; import :hardware;
import :resources; import :resources;
import :control;
import :data;
export namespace vulkan_render { export namespace vulkan_render {
@ -27,4 +29,14 @@ export namespace vulkan_render {
static void SetupFrames(const Context &context, Swapchain &swapchain, const Surface &surface); static void SetupFrames(const Context &context, Swapchain &swapchain, const Surface &surface);
}; };
class DataFactory {
public:
static Texture<TextureMode::RENDER> CreateRenderTexture(
const Context &context,
const RenderCommands &commands,
std::uint32_t width, std::uint32_t height,
std::span<std::byte> textureData
);
};
} }

View File

@ -0,0 +1,89 @@
/**
* Do not include <RmlUi/Core.h> in module .cpp implementation
* to prevent MSVC internal compiler error
*/
module;
#include <RmlUi/Core.h>
export module rmlui_vulkan;
import vulkan_render;
import utils;
import std;
namespace ee_rml {
template <typename T> using lazy = std::optional<T>;
struct GeometryMesh {
lazy<vulkan_render::Buffer<
Rml::Vertex,
vk::BufferUsageFlagBits::eVertexBuffer,
vulkan_render::BufferMode::STAGING_ACCESS
>> vertices;
lazy<vulkan_render::Buffer<
std::uint32_t,
vk::BufferUsageFlagBits::eIndexBuffer,
vulkan_render::BufferMode::STAGING_ACCESS
>> indices;
};
}
export namespace ee_rml {
struct RenderContext {
vulkan_render::Context &context;
vulkan_render::RenderCommands &commands;
};
class RmlVulkan final : public Rml::RenderInterface {
public:
explicit RmlVulkan(RenderContext &renderContext) : renderer(renderContext) {}
Rml::CompiledGeometryHandle CompileGeometry(
const Rml::Span<const Rml::Vertex> vertices,
const Rml::Span<const std::int32_t> indices
) override {
GeometryMesh *mesh = Allocate<GeometryMesh>();
mesh->vertices.emplace(renderer.context, vertices.size());
mesh->indices.emplace(renderer.context, indices.size());
std::ranges::copy(vertices, mesh->vertices->content.data());
std::ranges::copy(indices, mesh->indices->content.data());
renderer.commands.SubmitStagingBuffer(*mesh->vertices);
renderer.commands.SubmitStagingBuffer(*mesh->indices);
return reinterpret_cast<Rml::CompiledGeometryHandle>(mesh);
}
void RenderGeometry(
Rml::CompiledGeometryHandle geometry,
Rml::Vector2f translation,
Rml::TextureHandle texture
) override {
}
void ReleaseGeometry(const Rml::CompiledGeometryHandle geometry) override {
Deallocate(reinterpret_cast<GeometryMesh*>(geometry));
};
Rml::TextureHandle LoadTexture(Rml::Vector2i &texture_dimensions, const Rml::String &source) override {return 0;}
Rml::TextureHandle GenerateTexture(
Rml::Span<const std::uint8_t> source,
Rml::Vector2i source_dimensions
) override {return 0;}
void ReleaseTexture(Rml::TextureHandle texture) override {}
void EnableScissorRegion(bool enable) override {}
void SetScissorRegion(Rml::Rectanglei region) override {}
private:
RenderContext &renderer;
};
}

View File

@ -74,6 +74,83 @@ void vulkan_render::RenderCommands::SubmitStagingBuffer(const BaseBuffer<BufferM
cmd.Submit(buffer.context.device.transfer); cmd.Submit(buffer.context.device.transfer);
} }
void vulkan_render::RenderCommands::SubmitTexture(
const BaseBuffer<BufferMode::STATIC> &buffer,
const Texture<TextureMode::PARTIAL> &texture
) const {
const Command cmd(buffer.context, graphicsPool);
const vk::BufferImageCopy copyRegion {
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1
},
.imageOffset = {0, 0, 0},
.imageExtent = {texture.width, texture.height, 1}
};
cmd.handle.copyBufferToImage(buffer.handle, texture.handle, vk::ImageLayout::eTransferDstOptimal, {copyRegion});
cmd.Submit(buffer.context.device.graphics);
}
void vulkan_render::RenderCommands::TranslateTextureLayout(
const Texture<TextureMode::PARTIAL> &texture,
const vk::ImageLayout &from,
const vk::ImageLayout &to
) const {
const Command cmd(texture.context, graphicsPool);
vk::ImageMemoryBarrier barrier {
.oldLayout = from,
.newLayout = to,
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.image = texture.handle,
.subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
},
};
vk::PipelineStageFlags sourceStage;
vk::PipelineStageFlags destinationStage;
if (from == vk::ImageLayout::eUndefined && to == vk::ImageLayout::eTransferDstOptimal) {
barrier.srcAccessMask = vk::AccessFlagBits::eNone;
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
destinationStage = vk::PipelineStageFlagBits::eTransfer;
} else if (from == vk::ImageLayout::eTransferDstOptimal && to == vk::ImageLayout::eReadOnlyOptimal) {
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
sourceStage = vk::PipelineStageFlagBits::eTransfer;
destinationStage = vk::PipelineStageFlagBits::eFragmentShader;
} else {
LOG(ERROR, "Unsupported layout transition: %d -> %d.", from, to);
}
cmd.handle.pipelineBarrier(
sourceStage, destinationStage,
vk::DependencyFlags(0),
nullptr,
nullptr,
{barrier}
);
cmd.Submit(texture.context.device.graphics);
}
vk::raii::CommandPool vulkan_render::RenderCommands::MakePool(const Context &context, const std::uint32_t queueFamily) { vk::raii::CommandPool vulkan_render::RenderCommands::MakePool(const Context &context, const std::uint32_t queueFamily) {
return context.device.handle.createCommandPool({ return context.device.handle.createCommandPool({
.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer, .flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer,

View File

@ -85,3 +85,33 @@ void vulkan_render::ResourcesFactory::SetupFrames(const Context &context, Swapch
} }
LOG(INFO, "Frame views are ready."); LOG(INFO, "Frame views are ready.");
} }
vulkan_render::Texture<vulkan_render::TextureMode::RENDER> vulkan_render::DataFactory::CreateRenderTexture(
const Context &context,
const RenderCommands &commands,
const std::uint32_t width, const std::uint32_t height,
const std::span<std::byte> textureData
) {
Texture<TextureMode::PARTIAL> texture(context, width, height, vk::Format::eR8G8B8A8Srgb);
const Buffer<
std::byte,
vk::BufferUsageFlagBits::eTransferSrc,
BufferMode::STATIC
> stagingBuffer(context, textureData);
commands.TranslateTextureLayout(
texture,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal
);
commands.SubmitTexture(stagingBuffer, texture);
commands.TranslateTextureLayout(
texture,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal
);
texture.Init(vk::ImageAspectFlagBits::eColor);
return std::move(texture);
}

View File

@ -0,0 +1,6 @@
// module;
//
// #include <RmlUi/Core.h>
//
// module rmlui_vulkan;
//