Replace the OpenGL rendering backend with Vulkan across the engine: - Graphics: add VulkanContext (instance/device/swapchain/renderpass with depth-stencil), port Canvas to nanovg-on-Vulkan and DebugGUI to the imgui Vulkan backend, drop GLEW. Wait for the Wayland configure handshake before the first present and rebuild the swapchain to the confirmed size so the window maps reliably on fractional-scale outputs. - RasterRender: reimplement RenderBuffer, shaders and object pipeline on Vulkan (offscreen VkImage target with MSAA resolve, SPIR-V pipelines, push-constant MVP, depth remap for Vulkan clip space). - Sketch3D: port the stroke renderer (geometry-shader line expansion) and stroke GPU buffers to Vulkan. - 3DEditor: port the path-tracer texture upload to a Vulkan staging upload and keep raster/path-tracer viewports oriented consistently. - Add a nanovg external-VkImage bridge plus a render-target registry so 3D render targets can be shown inside the 2D canvas. - Prefer the Wayland platform in GLFW regardless of DISPLAY. - Widgets: make BasicLayout size distribution converge with a sub-pixel epsilon instead of an exact-zero comparison, which could otherwise spin forever on fractional-scale displays and block the first frame. Vendor nanovg_vulkan and add a standalone VulkanGraphics module.
116 lines
3.3 KiB
C++
116 lines
3.3 KiB
C++
#include "RasterRender.hpp"
|
|
|
|
#include "GPUBuffers.hpp"
|
|
#include "VulkanObjectPipeline.hpp"
|
|
#include "VulkanRenderTarget.hpp"
|
|
#include "VulkanClipSpace.hpp"
|
|
#include "VulkanContext.hpp"
|
|
|
|
namespace tp {
|
|
|
|
class RasterRender::Context {
|
|
public:
|
|
VulkanContext* vulkan = nullptr;
|
|
VulkanObjectPipeline* defaultPipeline = nullptr;
|
|
VulkanObjectPipeline* solidPipeline = nullptr;
|
|
VkRenderPass builtForRenderPass = VK_NULL_HANDLE;
|
|
|
|
~Context() {
|
|
delete defaultPipeline;
|
|
delete solidPipeline;
|
|
}
|
|
|
|
void ensurePipelines(VulkanRenderTarget* target) {
|
|
if (defaultPipeline && builtForRenderPass == target->renderPass()) return;
|
|
|
|
delete defaultPipeline;
|
|
delete solidPipeline;
|
|
|
|
defaultPipeline = new VulkanObjectPipeline(vulkan, target->renderPass(), target->sampleCount(), false);
|
|
solidPipeline = new VulkanObjectPipeline(vulkan, target->renderPass(), target->sampleCount(), true);
|
|
builtForRenderPass = target->renderPass();
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
|
|
ObjectPushConstants makePushConstants(const Mat4F& cameraMat, const Object& object) {
|
|
Mat4F camera = vulkanCameraMatrix(cameraMat);
|
|
Mat4F basisMat = toMat4(object.mTopology.Basis);
|
|
Vec4F originPoint = toVec4(object.mTopology.Origin);
|
|
|
|
Mat4F mvp = camera * basisMat;
|
|
|
|
ObjectPushConstants constants;
|
|
constants.mvp = mvp.transpose();
|
|
constants.offset = camera * originPoint;
|
|
return constants;
|
|
}
|
|
}
|
|
|
|
RasterRender::RasterRender() {
|
|
mCtx = new Context();
|
|
mCtx->vulkan = VulkanContext::current();
|
|
}
|
|
|
|
RasterRender::~RasterRender() { delete mCtx; }
|
|
|
|
void RasterRender::resize(const Vec2<tp::ualni>& size) {
|
|
auto sizeF = Vec2F(size.x, size.y);
|
|
mRenderBuffer.resize(sizeF);
|
|
mTempBuffer.resize(sizeF);
|
|
}
|
|
|
|
uint4 RasterRender::getRenderBufferID() { return mRenderBuffer.texId(); }
|
|
|
|
Vec2F RasterRender::getBufferSize() { return mRenderBuffer.getSize(); }
|
|
|
|
void RasterRender::renderDefault(const Scene& geometry) {
|
|
mRenderBuffer.mClearCol = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
|
|
mRenderBuffer.beginDraw();
|
|
|
|
auto target = static_cast<VulkanRenderTarget*>(mRenderBuffer.renderTarget());
|
|
auto cmd = static_cast<VkCommandBuffer>(mRenderBuffer.activeCommandBuffer());
|
|
mCtx->ensurePipelines(target);
|
|
|
|
mCtx->defaultPipeline->bind(cmd);
|
|
|
|
Mat4F cameraMat = geometry.mCamera.calculateTransformationMatrix();
|
|
|
|
for (auto object : geometry.mObjects) {
|
|
mCtx->defaultPipeline->pushUniforms(cmd, makePushConstants(cameraMat, object.data()));
|
|
object->mGUPBuffers->drawCall(cmd);
|
|
}
|
|
|
|
mRenderBuffer.endDraw();
|
|
}
|
|
|
|
void RasterRender::renderOutline(const Camera& camera, const Object& object) {
|
|
mRenderBuffer.beginDraw();
|
|
|
|
auto target = static_cast<VulkanRenderTarget*>(mRenderBuffer.renderTarget());
|
|
auto cmd = static_cast<VkCommandBuffer>(mRenderBuffer.activeCommandBuffer());
|
|
mCtx->ensurePipelines(target);
|
|
|
|
mCtx->solidPipeline->bind(cmd);
|
|
mCtx->solidPipeline->pushUniforms(cmd, makePushConstants(camera.calculateTransformationMatrix(), object));
|
|
object.mGUPBuffers->drawCall(cmd);
|
|
|
|
mRenderBuffer.endDraw();
|
|
}
|
|
|
|
void RasterRender::beginRender(const Scene& geometry, const Vec2<tp::ualni>& size) {
|
|
resize(size);
|
|
|
|
for (auto object : geometry.mObjects) {
|
|
if (!object->mGUPBuffers) {
|
|
object->mGUPBuffers = new ObjectBuffers(&object.data());
|
|
}
|
|
}
|
|
}
|
|
|
|
void RasterRender::endRender() {}
|
|
|
|
RenderBuffer* RasterRender::getRenderBuffer() { return &mRenderBuffer; }
|
|
}
|