Files
WickedEngine/WickedEngine/wiRenderPath2D.cpp
T
Turánszki János 39a5db515a fixes
2025-06-29 06:54:10 +02:00

540 lines
12 KiB
C++

#include "wiRenderPath2D.h"
#include "wiResourceManager.h"
#include "wiSprite.h"
#include "wiSpriteFont.h"
#include "wiRenderer.h"
using namespace wi::graphics;
namespace wi
{
void RenderPath2D::DeleteGPUResources()
{
current_buffersize = {};
current_layoutscale = 0; // invalidate layout
rtFinal = {};
rtFinal_MSAA = {};
rtStencilExtracted = {};
stencilScaled = {};
}
void RenderPath2D::ResizeBuffers()
{
current_buffersize = GetInternalResolution();
current_layoutscale = 0; // invalidate layout
GraphicsDevice* device = wi::graphics::GetDevice();
const uint32_t sampleCount = std::max(getMSAASampleCount(), getMSAASampleCount2D());
const Texture* dsv = GetDepthStencil();
if (dsv != nullptr && (resolutionScale != 1.0f || dsv->GetDesc().sample_count != sampleCount))
{
TextureDesc desc = GetDepthStencil()->GetDesc();
desc.layout = ResourceState::SHADER_RESOURCE;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
desc.format = Format::R8_UINT;
desc.layout = ResourceState::SHADER_RESOURCE;
device->CreateTexture(&desc, nullptr, &rtStencilExtracted);
device->SetName(&rtStencilExtracted, "rtStencilExtracted");
desc.width = GetPhysicalWidth();
desc.height = GetPhysicalHeight();
desc.sample_count = sampleCount;
desc.bind_flags = BindFlag::DEPTH_STENCIL;
desc.format = Format::D32_FLOAT_S8X24_UINT; // D24 was erroring on Vulkan AMD though here it would be enough
desc.layout = ResourceState::DEPTHSTENCIL;
device->CreateTexture(&desc, nullptr, &stencilScaled);
device->SetName(&stencilScaled, "stencilScaled");
}
else
{
rtStencilExtracted = {};
stencilScaled = {};
}
{
TextureDesc desc;
desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE;
desc.format = Format::R8G8B8A8_UNORM;
desc.width = GetPhysicalWidth();
desc.height = GetPhysicalHeight();
device->CreateTexture(&desc, nullptr, &rtFinal);
device->SetName(&rtFinal, "rtFinal");
if (sampleCount > 1)
{
desc.sample_count = sampleCount;
desc.bind_flags = BindFlag::RENDER_TARGET;
desc.misc_flags = ResourceMiscFlag::TRANSIENT_ATTACHMENT;
desc.layout = ResourceState::RENDERTARGET;
device->CreateTexture(&desc, nullptr, &rtFinal_MSAA);
device->SetName(&rtFinal_MSAA, "rtFinal_MSAA");
}
}
}
void RenderPath2D::ResizeLayout()
{
current_layoutscale = GetDPIScaling();
}
void RenderPath2D::Update(float dt)
{
XMUINT2 internalResolution = GetInternalResolution();
if (current_buffersize.x != internalResolution.x || current_buffersize.y != internalResolution.y)
{
ResizeBuffers();
}
if (current_layoutscale != GetDPIScaling())
{
ResizeLayout();
}
GetGUI().Update(*this, dt);
video_decodes.clear();
for (auto& x : layers)
{
for (auto& y : x.items)
{
if (y.sprite != nullptr)
{
y.sprite->Update(dt);
}
if (y.font != nullptr)
{
y.font->Update(dt);
}
if (y.videoinstance != nullptr)
{
wi::video::UpdateVideo(y.videoinstance, dt);
if (wi::video::IsDecodingRequired(y.videoinstance))
{
video_decodes.push_back(y.videoinstance);
}
}
}
}
RenderPath::Update(dt);
}
void RenderPath2D::FixedUpdate()
{
for (auto& x : layers)
{
for (auto& y : x.items)
{
if (y.sprite != nullptr)
{
y.sprite->FixedUpdate();
}
if (y.font != nullptr)
{
y.font->FixedUpdate();
}
}
}
RenderPath::FixedUpdate();
}
void RenderPath2D::PreRender()
{
XMUINT2 internalResolution = GetInternalResolution();
if (current_buffersize.x != internalResolution.x || current_buffersize.y != internalResolution.y)
{
ResizeBuffers();
}
RenderPath::PreRender();
}
void RenderPath2D::Render() const
{
if (!rtFinal.IsValid())
{
// Since 0.71.694: PreRender must be called before Render() because it sets up rendering resources!
// The proper fix is to call PreRender() yourself for a manually managed RenderPath3D
// But if you don't do that, as a last resort it will be called here using const_cast
assert(0);
const_cast<RenderPath2D*>(this)->PreRender();
}
GraphicsDevice* device = wi::graphics::GetDevice();
CommandList video_cmd;
if (!video_decodes.empty())
{
video_cmd = device->BeginCommandList(QUEUE_VIDEO_DECODE);
device->EventBegin("RenderPath2D video decoding", video_cmd);
for (auto& x : video_decodes)
{
wi::video::DecodeVideo(x, video_cmd);
}
device->EventEnd(video_cmd);
}
CommandList cmd = device->BeginCommandList();
device->EventBegin("RenderPath2D::Render", cmd);
wi::image::SetCanvas(*this);
wi::font::SetCanvas(*this);
if (!video_decodes.empty())
{
device->WaitCommandList(cmd, video_cmd);
device->EventBegin("RenderPath2D video resolving", cmd);
for (auto& x : video_decodes)
{
wi::video::ResolveVideoToRGB(x, cmd);
}
device->EventEnd(cmd);
}
wi::renderer::ProcessDeferredTextureRequests(cmd);
if (GetGUIBlurredBackground() != nullptr)
{
wi::image::SetBackground(*GetGUIBlurredBackground());
}
const Texture* dsv = GetDepthStencil();
if (rtStencilExtracted.IsValid())
{
wi::renderer::ExtractStencil(*dsv, rtStencilExtracted, cmd);
}
RenderPassImage rp[4];
uint32_t rp_count = 0;
if (rtFinal_MSAA.IsValid())
{
// MSAA:
rp[rp_count++] = RenderPassImage::RenderTarget(
&rtFinal_MSAA,
RenderPassImage::LoadOp::CLEAR,
RenderPassImage::StoreOp::DONTCARE,
ResourceState::RENDERTARGET,
ResourceState::RENDERTARGET
);
rp[rp_count++] = RenderPassImage::Resolve(&rtFinal);
}
else
{
// Single sample:
rp[rp_count++] = RenderPassImage::RenderTarget(&rtFinal, RenderPassImage::LoadOp::CLEAR);
}
if (stencilScaled.IsValid())
{
// Scaled stencil:
rp[rp_count++] = RenderPassImage::DepthStencil(&stencilScaled, RenderPassImage::LoadOp::CLEAR, RenderPassImage::StoreOp::DONTCARE);
}
else if (dsv != nullptr)
{
// Native stencil:
rp[rp_count++] = RenderPassImage::DepthStencil(dsv, RenderPassImage::LoadOp::LOAD, RenderPassImage::StoreOp::STORE);
}
device->RenderPassBegin(rp, rp_count, cmd);
Viewport vp;
vp.width = (float)rtFinal.GetDesc().width;
vp.height = (float)rtFinal.GetDesc().height;
device->BindViewports(1, &vp, cmd);
Rect rect;
rect.left = 0;
rect.right = (int32_t)rtFinal.GetDesc().width;
rect.top = 0;
rect.bottom = (int32_t)rtFinal.GetDesc().height;
device->BindScissorRects(1, &rect, cmd);
bool stencil_scaled = false;
device->EventBegin("Layers", cmd);
for (auto& x : layers)
{
for (auto& y : x.items)
{
if (y.sprite != nullptr)
{
if (!stencil_scaled && y.sprite->params.stencilComp != wi::image::STENCILMODE_DISABLED && stencilScaled.IsValid())
{
// Only need a scaled stencil mask if there are any stenciled sprites, and only once is enough before the first one
stencil_scaled = true;
wi::renderer::ScaleStencilMask(vp, rtStencilExtracted, cmd);
}
if (y.videoinstance != nullptr)
{
y.sprite->textureResource.SetTexture(y.videoinstance->GetCurrentFrameTexture());
}
y.sprite->Draw(cmd);
}
if (y.font != nullptr)
{
y.font->Draw(cmd);
}
}
}
device->EventEnd(cmd);
GetGUI().Render(*this, cmd);
device->RenderPassEnd(cmd);
device->EventEnd(cmd);
RenderPath::Render();
}
void RenderPath2D::Compose(CommandList cmd) const
{
GraphicsDevice* device = wi::graphics::GetDevice();
device->EventBegin("RenderPath2D::Compose", cmd);
wi::image::Params fx;
fx.enableFullScreen();
fx.blendFlag = wi::enums::BLENDMODE_PREMULTIPLIED;
if (colorspace != ColorSpace::SRGB)
{
// Convert the regular SRGB result of the render path to linear space for HDR compositing:
fx.enableLinearOutputMapping(hdr_scaling);
}
wi::image::Draw(&GetRenderResult(), fx, cmd);
device->EventEnd(cmd);
RenderPath::Compose(cmd);
}
void RenderPath2D::AddSprite(wi::Sprite* sprite, const std::string& layer)
{
for (auto& x : layers)
{
if (x.name == layer)
{
x.items.emplace_back().sprite = sprite;
}
}
SortLayers();
}
void RenderPath2D::RemoveSprite(wi::Sprite* sprite)
{
for (auto& x : layers)
{
for (auto& y : x.items)
{
if (y.sprite == sprite)
{
y.sprite = nullptr;
y.videoinstance = nullptr;
}
}
}
CleanLayers();
}
void RenderPath2D::ClearSprites()
{
for (auto& x : layers)
{
for (auto& y : x.items)
{
y.sprite = nullptr;
y.videoinstance = nullptr;
}
}
CleanLayers();
}
int RenderPath2D::GetSpriteOrder(wi::Sprite* sprite)
{
for (auto& x : layers)
{
for (auto& y : x.items)
{
if (y.sprite == sprite)
{
return y.order;
}
}
}
return 0;
}
void RenderPath2D::AddFont(wi::SpriteFont* font, const std::string& layer)
{
for (auto& x : layers)
{
if (x.name == layer)
{
x.items.emplace_back().font = font;
}
}
SortLayers();
}
void RenderPath2D::RemoveFont(wi::SpriteFont* font)
{
for (auto& x : layers)
{
for (auto& y : x.items)
{
if (y.font == font)
{
y.font = nullptr;
}
}
}
CleanLayers();
}
void RenderPath2D::ClearFonts()
{
for (auto& x : layers)
{
for (auto& y : x.items)
{
y.font = nullptr;
}
}
CleanLayers();
}
int RenderPath2D::GetFontOrder(wi::SpriteFont* font)
{
for (auto& x : layers)
{
for (auto& y : x.items)
{
if (y.font == font)
{
return y.order;
}
}
}
return 0;
}
void RenderPath2D::AddVideoSprite(wi::video::VideoInstance* videoinstance, wi::Sprite* sprite, const std::string& layer)
{
for (auto& x : layers)
{
if (x.name == layer)
{
RenderItem2D& item = x.items.emplace_back();
item.sprite = sprite;
item.videoinstance = videoinstance;
}
}
SortLayers();
}
void RenderPath2D::AddLayer(const std::string& name)
{
for (auto& x : layers)
{
if (!x.name.compare(name))
return;
}
RenderLayer2D layer;
layer.name = name;
layer.order = (int)layers.size();
layers.push_back(layer);
layers.back().items.clear();
}
void RenderPath2D::SetLayerOrder(const std::string& name, int order)
{
for (auto& x : layers)
{
if (!x.name.compare(name))
{
x.order = order;
break;
}
}
SortLayers();
}
void RenderPath2D::SetSpriteOrder(wi::Sprite* sprite, int order)
{
for (auto& x : layers)
{
for (auto& y : x.items)
{
if (y.sprite == sprite)
{
y.order = order;
}
}
}
SortLayers();
}
void RenderPath2D::SetFontOrder(wi::SpriteFont* font, int order)
{
for (auto& x : layers)
{
for (auto& y : x.items)
{
if (y.font == font)
{
y.order = order;
}
}
}
SortLayers();
}
void RenderPath2D::SortLayers()
{
if (layers.empty())
{
return;
}
for (size_t i = 0; i < layers.size() - 1; ++i)
{
for (size_t j = i + 1; j < layers.size(); ++j)
{
if (layers[i].order > layers[j].order)
{
RenderLayer2D swap = layers[i];
layers[i] = layers[j];
layers[j] = swap;
}
}
}
for (auto& x : layers)
{
if (x.items.empty())
{
continue;
}
for (size_t i = 0; i < x.items.size() - 1; ++i)
{
for (size_t j = i + 1; j < x.items.size(); ++j)
{
if (x.items[i].order > x.items[j].order)
{
RenderItem2D swap = x.items[i];
x.items[i] = x.items[j];
x.items[j] = swap;
}
}
}
}
}
void RenderPath2D::CleanLayers()
{
for (auto& x : layers)
{
if (x.items.empty())
{
continue;
}
wi::vector<RenderItem2D> itemsToRetain(0);
for (auto& y : x.items)
{
if (y.sprite != nullptr || y.font != nullptr || y.videoinstance != nullptr)
{
itemsToRetain.push_back(y);
}
}
x.items.clear();
x.items = itemsToRetain;
}
}
}