- added the softpoly and Vulkan backend code fron GZDoom.

Not hooked up yet.
This commit is contained in:
Christoph Oelckers 2020-05-31 10:53:11 +02:00
parent 2841154683
commit 611dad7f69
195 changed files with 135802 additions and 38 deletions

View file

@ -0,0 +1,162 @@
/*
** Softpoly backend
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include "poly_buffers.h"
#include "poly_framebuffer.h"
#include "poly_renderstate.h"
#include "poly_thread.h"
#include "engineerrors.h"
PolyBuffer *PolyBuffer::First = nullptr;
PolyBuffer::PolyBuffer()
{
Next = First;
First = this;
if (Next) Next->Prev = this;
}
PolyBuffer::~PolyBuffer()
{
if (Next) Next->Prev = Prev;
if (Prev) Prev->Next = Next;
else First = Next;
auto fb = GetPolyFrameBuffer();
if (fb && !mData.empty())
fb->FrameDeleteList.Buffers.push_back(std::move(mData));
}
void PolyBuffer::ResetAll()
{
for (PolyBuffer *cur = PolyBuffer::First; cur; cur = cur->Next)
cur->Reset();
}
void PolyBuffer::Reset()
{
}
void PolyBuffer::SetData(size_t size, const void *data, bool staticdata)
{
mData.resize(size);
map = mData.data();
if (data)
memcpy(map, data, size);
buffersize = size;
}
void PolyBuffer::SetSubData(size_t offset, size_t size, const void *data)
{
memcpy(static_cast<uint8_t*>(map) + offset, data, size);
}
void PolyBuffer::Resize(size_t newsize)
{
mData.resize(newsize);
buffersize = newsize;
map = mData.data();
}
void PolyBuffer::Map()
{
}
void PolyBuffer::Unmap()
{
}
void *PolyBuffer::Lock(unsigned int size)
{
if (mData.size() < (size_t)size)
Resize(size);
return map;
}
void PolyBuffer::Unlock()
{
}
/////////////////////////////////////////////////////////////////////////////
void PolyVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs)
{
VertexFormat = GetPolyFrameBuffer()->GetRenderState()->GetVertexFormat(numBindingPoints, numAttributes, stride, attrs);
}
/////////////////////////////////////////////////////////////////////////////
void PolyVertexInputAssembly::Load(PolyTriangleThreadData *thread, const void *vertices, int index)
{
const uint8_t *vertex = static_cast<const uint8_t*>(vertices) + mStride * index;
const float *attrVertex = reinterpret_cast<const float*>(vertex + mOffsets[VATTR_VERTEX]);
const float *attrTexcoord = reinterpret_cast<const float*>(vertex + mOffsets[VATTR_TEXCOORD]);
const uint8_t *attrColor = reinterpret_cast<const uint8_t*>(vertex + mOffsets[VATTR_COLOR]);
const uint32_t* attrNormal = reinterpret_cast<const uint32_t*>(vertex + mOffsets[VATTR_NORMAL]);
const uint32_t* attrNormal2 = reinterpret_cast<const uint32_t*>(vertex + mOffsets[VATTR_NORMAL2]);
thread->mainVertexShader.aPosition = { attrVertex[0], attrVertex[1], attrVertex[2], 1.0f };
thread->mainVertexShader.aTexCoord = { attrTexcoord[0], attrTexcoord[1] };
if ((UseVertexData & 1) == 0)
{
const auto &c = thread->mainVertexShader.Data.uVertexColor;
thread->mainVertexShader.aColor.X = c.X;
thread->mainVertexShader.aColor.Y = c.Y;
thread->mainVertexShader.aColor.Z = c.Z;
thread->mainVertexShader.aColor.W = c.W;
}
else
{
thread->mainVertexShader.aColor.X = attrColor[0] * (1.0f / 255.0f);
thread->mainVertexShader.aColor.Y = attrColor[1] * (1.0f / 255.0f);
thread->mainVertexShader.aColor.Z = attrColor[2] * (1.0f / 255.0f);
thread->mainVertexShader.aColor.W = attrColor[3] * (1.0f / 255.0f);
}
if ((UseVertexData & 2) == 0)
{
const auto &n = thread->mainVertexShader.Data.uVertexNormal;
thread->mainVertexShader.aNormal = FVector4(n.X, n.Y, n.Z, 1.0);
thread->mainVertexShader.aNormal2 = thread->mainVertexShader.aNormal;
}
else
{
int n = *attrNormal;
int n2 = *attrNormal2;
float x = ((n << 22) >> 22) / 512.0f;
float y = ((n << 12) >> 22) / 512.0f;
float z = ((n << 2) >> 22) / 512.0f;
float x2 = ((n2 << 22) >> 22) / 512.0f;
float y2 = ((n2 << 12) >> 22) / 512.0f;
float z2 = ((n2 << 2) >> 22) / 512.0f;
thread->mainVertexShader.aNormal = FVector4(x, y, z, 0.0f);
thread->mainVertexShader.aNormal2 = FVector4(x2, y2, z2, 0.0f);
}
}
/////////////////////////////////////////////////////////////////////////////
void PolyDataBuffer::BindRange(FRenderState *state, size_t start, size_t length)
{
static_cast<PolyRenderState*>(state)->Bind(this, (uint32_t)start, (uint32_t)length);
}

View file

@ -0,0 +1,79 @@
#pragma once
#include "hwrenderer/data/buffers.h"
#include "polyrenderer/drawers/poly_triangle.h"
#include "tarray.h"
#include <vector>
#ifdef _MSC_VER
// silence bogus warning C4250: 'PolyVertexBuffer': inherits 'PolyBuffer::PolyBuffer::SetData' via dominance
// According to internet infos, the warning is erroneously emitted in this case.
#pragma warning(disable:4250)
#endif
class PolyBuffer : virtual public IBuffer
{
public:
PolyBuffer();
~PolyBuffer();
static void ResetAll();
void Reset();
void SetData(size_t size, const void *data, bool staticdata) override;
void SetSubData(size_t offset, size_t size, const void *data) override;
void Resize(size_t newsize) override;
void Map() override;
void Unmap() override;
void *Lock(unsigned int size) override;
void Unlock() override;
private:
static PolyBuffer *First;
PolyBuffer *Prev = nullptr;
PolyBuffer *Next = nullptr;
std::vector<uint32_t> mData;
};
class PolyVertexInputAssembly final : public PolyInputAssembly
{
public:
size_t mOffsets[VATTR_MAX] = {};
size_t mStride = 0;
int NumBindingPoints;
size_t Stride;
std::vector<FVertexBufferAttribute> Attrs;
int UseVertexData;
void Load(PolyTriangleThreadData *thread, const void *vertices, int index) override;
};
class PolyVertexBuffer : public IVertexBuffer, public PolyBuffer
{
public:
PolyVertexBuffer() { }
void SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs) override;
PolyVertexInputAssembly *VertexFormat = nullptr;
};
class PolyIndexBuffer : public IIndexBuffer, public PolyBuffer
{
public:
PolyIndexBuffer() { }
};
class PolyDataBuffer : public IDataBuffer, public PolyBuffer
{
public:
PolyDataBuffer(int bindingpoint, bool ssbo, bool needresize) : bindingpoint(bindingpoint)
{
}
void BindRange(FRenderState *state, size_t start, size_t length) override;
int bindingpoint;
};

View file

@ -0,0 +1,430 @@
/*
** Softpoly backend
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include "v_video.h"
#include "m_png.h"
#include "templates.h"
#include "r_videoscale.h"
#include "i_time.h"
#include "v_text.h"
#include "i_video.h"
#include "v_draw.h"
#include "colormaps.h"
#include "hw_clock.h"
#include "hw_vrmodes.h"
#include "hw_cvars.h"
#include "hw_skydome.h"
#include "hwrenderer/data/hw_viewpointbuffer.h"
#include "flatvertices.h"
#include "hwrenderer/data/shaderuniforms.h"
#include "hw_lightbuffer.h"
#include "hwrenderer/postprocessing/hw_postprocess.h"
#include "poly_framebuffer.h"
#include "poly_buffers.h"
#include "poly_renderstate.h"
#include "poly_hwtexture.h"
#include "engineerrors.h"
void Draw2D(F2DDrawer *drawer, FRenderState &state);
extern int rendered_commandbuffers;
extern int current_rendered_commandbuffers;
extern bool gpuStatActive;
extern bool keepGpuStatActive;
extern FString gpuStatOutput;
PolyFrameBuffer::PolyFrameBuffer(void *hMonitor, bool fullscreen) : Super(hMonitor, fullscreen)
{
I_PolyPresentInit();
}
PolyFrameBuffer::~PolyFrameBuffer()
{
// screen is already null at this point, but PolyHardwareTexture::ResetAll needs it during clean up. Is there a better way we can do this?
auto tmp = screen;
screen = this;
PolyHardwareTexture::ResetAll();
PolyBuffer::ResetAll();
PPResource::ResetAll();
delete mScreenQuad.VertexBuffer;
delete mScreenQuad.IndexBuffer;
delete mVertexData;
delete mSkyData;
delete mViewpoints;
delete mLights;
mShadowMap.Reset();
screen = tmp;
I_PolyPresentDeinit();
}
void PolyFrameBuffer::InitializeState()
{
vendorstring = "Poly";
hwcaps = RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE;
glslversion = 4.50f;
uniformblockalignment = 1;
maxuniformblock = 0x7fffffff;
mRenderState.reset(new PolyRenderState());
mVertexData = new FFlatVertexBuffer(GetWidth(), GetHeight());
mSkyData = new FSkyVertexBuffer;
mViewpoints = new HWViewpointBuffer;
mLights = new FLightBuffer();
static const FVertexBufferAttribute format[] =
{
{ 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(ScreenQuadVertex, x) },
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(ScreenQuadVertex, u) },
{ 0, VATTR_COLOR, VFmt_Byte4, (int)myoffsetof(ScreenQuadVertex, color0) }
};
uint32_t indices[6] = { 0, 1, 2, 1, 3, 2 };
mScreenQuad.VertexBuffer = screen->CreateVertexBuffer();
mScreenQuad.VertexBuffer->SetFormat(1, 3, sizeof(ScreenQuadVertex), format);
mScreenQuad.IndexBuffer = screen->CreateIndexBuffer();
mScreenQuad.IndexBuffer->SetData(6 * sizeof(uint32_t), indices, false);
CheckCanvas();
}
void PolyFrameBuffer::CheckCanvas()
{
if (!mCanvas || mCanvas->GetWidth() != GetWidth() || mCanvas->GetHeight() != GetHeight())
{
FlushDrawCommands();
DrawerThreads::WaitForWorkers();
mCanvas.reset(new DCanvas(0, 0, true));
mCanvas->Resize(GetWidth(), GetHeight(), false);
mDepthStencil.reset();
mDepthStencil.reset(new PolyDepthStencil(GetWidth(), GetHeight()));
mRenderState->SetRenderTarget(GetCanvas(), GetDepthStencil(), true);
}
}
PolyCommandBuffer *PolyFrameBuffer::GetDrawCommands()
{
if (!mDrawCommands)
{
mDrawCommands.reset(new PolyCommandBuffer(&mFrameMemory));
mDrawCommands->SetLightBuffer(mLightBuffer->Memory());
}
return mDrawCommands.get();
}
void PolyFrameBuffer::FlushDrawCommands()
{
mRenderState->EndRenderPass();
if (mDrawCommands)
{
mDrawCommands->Submit();
mDrawCommands.reset();
}
}
void PolyFrameBuffer::Update()
{
twoD.Reset();
Flush3D.Reset();
Flush3D.Clock();
Draw2D();
twod->Clear();
Flush3D.Unclock();
FlushDrawCommands();
if (mCanvas)
{
int w = mCanvas->GetWidth();
int h = mCanvas->GetHeight();
int pixelsize = 4;
const uint8_t *src = (const uint8_t*)mCanvas->GetPixels();
int pitch = 0;
uint8_t *dst = I_PolyPresentLock(w, h, cur_vsync, pitch);
if (dst)
{
#if 1
auto copyqueue = std::make_shared<DrawerCommandQueue>(&mFrameMemory);
copyqueue->Push<MemcpyCommand>(dst, pitch / pixelsize, src, w, h, w, pixelsize);
DrawerThreads::Execute(copyqueue);
#else
for (int y = 0; y < h; y++)
{
memcpy(dst + y * pitch, src + y * w * pixelsize, w * pixelsize);
}
#endif
DrawerThreads::WaitForWorkers();
I_PolyPresentUnlock(mOutputLetterbox.left, mOutputLetterbox.top, mOutputLetterbox.width, mOutputLetterbox.height);
}
FPSLimit();
}
DrawerThreads::WaitForWorkers();
mFrameMemory.Clear();
FrameDeleteList.Buffers.clear();
FrameDeleteList.Images.clear();
CheckCanvas();
Super::Update();
}
void PolyFrameBuffer::RenderTextureView(FCanvasTexture* tex, std::function<void(IntRect &)> renderFunc)
{
auto BaseLayer = static_cast<PolyHardwareTexture*>(tex->GetHardwareTexture(0, 0));
DCanvas *image = BaseLayer->GetImage(tex, 0, 0);
PolyDepthStencil *depthStencil = BaseLayer->GetDepthStencil(tex);
mRenderState->SetRenderTarget(image, depthStencil, false);
IntRect bounds;
bounds.left = bounds.top = 0;
bounds.width = std::min(tex->GetWidth(), image->GetWidth());
bounds.height = std::min(tex->GetHeight(), image->GetHeight());
renderFunc(bounds);
FlushDrawCommands();
DrawerThreads::WaitForWorkers();
mRenderState->SetRenderTarget(GetCanvas(), GetDepthStencil(), true);
tex->SetUpdated(true);
}
static uint8_t ToIntColorComponent(float v)
{
return clamp((int)(v * 255.0f + 0.5f), 0, 255);
}
void PolyFrameBuffer::PostProcessScene(bool swscene, int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D)
{
afterBloomDrawEndScene2D();
if (fixedcm >= CM_FIRSTSPECIALCOLORMAP && fixedcm < CM_MAXCOLORMAP)
{
FSpecialColormap* scm = &SpecialColormaps[fixedcm - CM_FIRSTSPECIALCOLORMAP];
mRenderState->SetViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
screen->mViewpoints->Set2D(*mRenderState, screen->GetWidth(), screen->GetHeight());
ScreenQuadVertex vertices[4] =
{
{ 0.0f, 0.0f, 0.0f, 0.0f },
{ (float)mScreenViewport.width, 0.0f, 1.0f, 0.0f },
{ 0.0f, (float)mScreenViewport.height, 0.0f, 1.0f },
{ (float)mScreenViewport.width, (float)mScreenViewport.height, 1.0f, 1.0f }
};
mScreenQuad.VertexBuffer->SetData(4 * sizeof(ScreenQuadVertex), vertices, false);
mRenderState->SetVertexBuffer(mScreenQuad.VertexBuffer, 0, 0);
mRenderState->SetIndexBuffer(mScreenQuad.IndexBuffer);
mRenderState->SetObjectColor(PalEntry(255, int(scm->ColorizeStart[0] * 127.5f), int(scm->ColorizeStart[1] * 127.5f), int(scm->ColorizeStart[2] * 127.5f)));
mRenderState->SetAddColor(PalEntry(255, int(scm->ColorizeEnd[0] * 127.5f), int(scm->ColorizeEnd[1] * 127.5f), int(scm->ColorizeEnd[2] * 127.5f)));
mRenderState->EnableDepthTest(false);
mRenderState->EnableMultisampling(false);
mRenderState->SetCulling(Cull_None);
mRenderState->SetScissor(-1, -1, -1, -1);
mRenderState->SetColor(1, 1, 1, 1);
mRenderState->AlphaFunc(Alpha_GEqual, 0.f);
mRenderState->EnableTexture(false);
mRenderState->SetColormapShader(true);
mRenderState->DrawIndexed(DT_Triangles, 0, 6);
mRenderState->SetColormapShader(false);
mRenderState->SetObjectColor(0xffffffff);
mRenderState->SetAddColor(0);
mRenderState->SetVertexBuffer(screen->mVertexData);
mRenderState->EnableTexture(true);
mRenderState->ResetColor();
}
}
void PolyFrameBuffer::SetVSync(bool vsync)
{
cur_vsync = vsync;
}
FRenderState* PolyFrameBuffer::RenderState()
{
return mRenderState.get();
}
void PolyFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation)
{
if (mat->Source()->GetUseType() == ETextureType::SWCanvas) return;
MaterialLayerInfo* layer;
auto systex = static_cast<PolyHardwareTexture*>(mat->GetLayer(0, translation, &layer));
systex->GetImage(layer->layerTexture, translation, layer->scaleFlags);
int numLayers = mat->NumLayers();
for (int i = 1; i < numLayers; i++)
{
auto systex = static_cast<PolyHardwareTexture*>(mat->GetLayer(i, 0, &layer));
systex->GetImage(layer->layerTexture, 0, layer->scaleFlags); // fixme: Upscale flags must be disabled for certain layers.
}
}
IHardwareTexture *PolyFrameBuffer::CreateHardwareTexture(int)
{
return new PolyHardwareTexture();
}
IVertexBuffer *PolyFrameBuffer::CreateVertexBuffer()
{
return new PolyVertexBuffer();
}
IIndexBuffer *PolyFrameBuffer::CreateIndexBuffer()
{
return new PolyIndexBuffer();
}
IDataBuffer *PolyFrameBuffer::CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize)
{
IDataBuffer *buffer = new PolyDataBuffer(bindingpoint, ssbo, needsresize);
if (bindingpoint == LIGHTBUF_BINDINGPOINT)
mLightBuffer = buffer;
return buffer;
}
void PolyFrameBuffer::SetTextureFilterMode()
{
}
void PolyFrameBuffer::BlurScene(float amount)
{
}
void PolyFrameBuffer::UpdatePalette()
{
}
FTexture *PolyFrameBuffer::WipeStartScreen()
{
SetViewportRects(nullptr);
auto tex = new FWrapperTexture(mScreenViewport.width, mScreenViewport.height, 1);
auto systex = static_cast<PolyHardwareTexture*>(tex->GetSystemTexture());
systex->CreateWipeTexture(mScreenViewport.width, mScreenViewport.height, "WipeStartScreen");
return tex;
}
FTexture *PolyFrameBuffer::WipeEndScreen()
{
Draw2D();
twod->Clear();
auto tex = new FWrapperTexture(mScreenViewport.width, mScreenViewport.height, 1);
auto systex = static_cast<PolyHardwareTexture*>(tex->GetSystemTexture());
systex->CreateWipeTexture(mScreenViewport.width, mScreenViewport.height, "WipeEndScreen");
return tex;
}
TArray<uint8_t> PolyFrameBuffer::GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma)
{
int w = SCREENWIDTH;
int h = SCREENHEIGHT;
TArray<uint8_t> ScreenshotBuffer(w * h * 3, true);
const uint8_t* pixels = GetCanvas()->GetPixels();
int dindex = 0;
// Convert to RGB
for (int y = 0; y < h; y++)
{
int sindex = y * w * 4;
for (int x = 0; x < w; x++)
{
ScreenshotBuffer[dindex ] = pixels[sindex + 2];
ScreenshotBuffer[dindex + 1] = pixels[sindex + 1];
ScreenshotBuffer[dindex + 2] = pixels[sindex ];
dindex += 3;
sindex += 4;
}
}
pitch = w * 3;
color_type = SS_RGB;
gamma = 1.0f;
return ScreenshotBuffer;
}
void PolyFrameBuffer::BeginFrame()
{
SetViewportRects(nullptr);
CheckCanvas();
#if 0
swrenderer::R_InitFuzzTable(GetCanvas()->GetPitch());
static int next_random = 0;
swrenderer::fuzzpos = (swrenderer::fuzzpos + swrenderer::fuzz_random_x_offset[next_random] * FUZZTABLE / 100) % FUZZTABLE;
next_random++;
if (next_random == FUZZ_RANDOM_X_SIZE)
next_random = 0;
#endif
}
void PolyFrameBuffer::Draw2D()
{
::Draw2D(twod, *mRenderState);
}
unsigned int PolyFrameBuffer::GetLightBufferBlockSize() const
{
return mLights->GetBlockSize();
}
void PolyFrameBuffer::UpdateShadowMap()
{
}
void PolyFrameBuffer::AmbientOccludeScene(float m5)
{
//mPostprocess->AmbientOccludeScene(m5);
}

View file

@ -0,0 +1,98 @@
#pragma once
#include "gl_sysfb.h"
#include "r_memory.h"
#include "r_thread.h"
#include "poly_triangle.h"
struct FRenderViewpoint;
class PolyDataBuffer;
class PolyRenderState;
class SWSceneDrawer;
class PolyFrameBuffer : public SystemBaseFrameBuffer
{
typedef SystemBaseFrameBuffer Super;
public:
RenderMemory *GetFrameMemory() { return &mFrameMemory; }
PolyRenderState *GetRenderState() { return mRenderState.get(); }
DCanvas *GetCanvas() override { return mCanvas.get(); }
PolyDepthStencil *GetDepthStencil() { return mDepthStencil.get(); }
PolyCommandBuffer *GetDrawCommands();
void FlushDrawCommands();
unsigned int GetLightBufferBlockSize() const;
PolyFrameBuffer(void *hMonitor, bool fullscreen);
~PolyFrameBuffer();
void Update() override;
bool IsPoly() override { return true; }
void InitializeState() override;
FRenderState* RenderState() override;
void PrecacheMaterial(FMaterial *mat, int translation) override;
void UpdatePalette() override;
void SetTextureFilterMode() override;
void BeginFrame() override;
void BlurScene(float amount) override;
void PostProcessScene(bool swscene, int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D) override;
void AmbientOccludeScene(float m5) override;
//void SetSceneRenderTarget(bool useSSAO) override;
IHardwareTexture *CreateHardwareTexture(int) override;
IVertexBuffer *CreateVertexBuffer() override;
IIndexBuffer *CreateIndexBuffer() override;
IDataBuffer *CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize) override;
FTexture *WipeStartScreen() override;
FTexture *WipeEndScreen() override;
TArray<uint8_t> GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma) override;
void SetVSync(bool vsync) override;
void Draw2D() override;
struct DeleteList
{
std::vector<std::vector<uint32_t>> Buffers;
std::vector<std::unique_ptr<DCanvas>> Images;
} FrameDeleteList;
private:
void RenderTextureView(FCanvasTexture* tex, std::function<void(IntRect &)> renderFunc) override;
void UpdateShadowMap() override;
void CheckCanvas();
IDataBuffer *mLightBuffer = nullptr;
std::unique_ptr<PolyRenderState> mRenderState;
std::unique_ptr<DCanvas> mCanvas;
std::unique_ptr<PolyDepthStencil> mDepthStencil;
std::unique_ptr<PolyCommandBuffer> mDrawCommands;
RenderMemory mFrameMemory;
struct ScreenQuadVertex
{
float x, y, z;
float u, v;
PalEntry color0;
ScreenQuadVertex() = default;
ScreenQuadVertex(float x, float y, float u, float v) : x(x), y(y), z(1.0f), u(u), v(v), color0(0xffffffff) { }
};
struct ScreenQuad
{
IVertexBuffer* VertexBuffer = nullptr;
IIndexBuffer* IndexBuffer = nullptr;
} mScreenQuad;
bool cur_vsync = false;
};
inline PolyFrameBuffer *GetPolyFrameBuffer() { return static_cast<PolyFrameBuffer*>(screen); }

View file

@ -0,0 +1,143 @@
/*
** Softpoly backend
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include "templates.h"
#include "c_cvars.h"
#include "hw_material.h"
#include "hw_cvars.h"
#include "hw_renderstate.h"
#include "poly_framebuffer.h"
#include "poly_hwtexture.h"
PolyHardwareTexture *PolyHardwareTexture::First = nullptr;
PolyHardwareTexture::PolyHardwareTexture()
{
Next = First;
First = this;
if (Next) Next->Prev = this;
}
PolyHardwareTexture::~PolyHardwareTexture()
{
if (Next) Next->Prev = Prev;
if (Prev) Prev->Next = Next;
else First = Next;
Reset();
}
void PolyHardwareTexture::ResetAll()
{
for (PolyHardwareTexture *cur = PolyHardwareTexture::First; cur; cur = cur->Next)
cur->Reset();
}
void PolyHardwareTexture::Reset()
{
if (auto fb = GetPolyFrameBuffer())
{
auto &deleteList = fb->FrameDeleteList;
if (mCanvas) deleteList.Images.push_back(std::move(mCanvas));
}
}
DCanvas *PolyHardwareTexture::GetImage(FTexture *tex, int translation, int flags)
{
if (!mCanvas)
CreateImage(tex, translation, flags);
return mCanvas.get();
}
PolyDepthStencil *PolyHardwareTexture::GetDepthStencil(FTexture *tex)
{
if (!mDepthStencil)
{
int w = tex->GetWidth();
int h = tex->GetHeight();
mDepthStencil.reset(new PolyDepthStencil(w, h));
}
return mDepthStencil.get();
}
void PolyHardwareTexture::AllocateBuffer(int w, int h, int texelsize)
{
if (!mCanvas || mCanvas->GetWidth() != w || mCanvas->GetHeight() != h)
{
mCanvas.reset(new DCanvas(0, 0, texelsize == 4));
mCanvas->Resize(w, h, false);
bufferpitch = mCanvas->GetPitch();
}
}
uint8_t *PolyHardwareTexture::MapBuffer()
{
return mCanvas->GetPixels();
}
unsigned int PolyHardwareTexture::CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, const char *name)
{
return 0;
}
void PolyHardwareTexture::CreateWipeTexture(int w, int h, const char *name)
{
if (!mCanvas || mCanvas->GetWidth() != w || mCanvas->GetHeight() != h)
{
mCanvas.reset(new DCanvas(0, 0, true));
mCanvas->Resize(w, h, false);
}
auto fb = static_cast<PolyFrameBuffer*>(screen);
fb->FlushDrawCommands();
DrawerThreads::WaitForWorkers();
uint32_t* dest = (uint32_t*)mCanvas->GetPixels();
uint32_t* src = (uint32_t*)fb->GetCanvas()->GetPixels();
int dpitch = mCanvas->GetPitch();
int spitch = fb->GetCanvas()->GetPitch();
int pixelsize = 4;
for (int y = 0; y < h; y++)
{
memcpy(dest + dpitch * (h - 1 - y), src + spitch * y, w * pixelsize);
}
}
void PolyHardwareTexture::CreateImage(FTexture *tex, int translation, int flags)
{
mCanvas.reset(new DCanvas(0, 0, true));
if (!tex->isHardwareCanvas())
{
FTextureBuffer texbuffer = tex->CreateTexBuffer(translation, flags | CTF_ProcessData);
mCanvas->Resize(texbuffer.mWidth, texbuffer.mHeight, false);
memcpy(mCanvas->GetPixels(), texbuffer.mBuffer, texbuffer.mWidth * texbuffer.mHeight * 4);
}
else
{
int w = tex->GetWidth();
int h = tex->GetHeight();
mCanvas->Resize(w, h, false);
}
}

View file

@ -0,0 +1,45 @@
#pragma once
#ifdef LoadImage
#undef LoadImage
#endif
#define SHADED_TEXTURE -1
#define DIRECT_PALETTE -2
#include "tarray.h"
#include "hw_ihwtexture.h"
#include "volk/volk.h"
struct FMaterialState;
class PolyBuffer;
class PolyHardwareTexture : public IHardwareTexture
{
public:
PolyHardwareTexture();
~PolyHardwareTexture();
static void ResetAll();
void Reset();
DCanvas *GetImage(FTexture *tex, int translation, int flags);
PolyDepthStencil *GetDepthStencil(FTexture *tex);
// Software renderer stuff
void AllocateBuffer(int w, int h, int texelsize) override;
uint8_t *MapBuffer() override;
unsigned int CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, const char *name) override;
// Wipe screen
void CreateWipeTexture(int w, int h, const char *name);
private:
void CreateImage(FTexture *tex, int translation, int flags);
static PolyHardwareTexture *First;
PolyHardwareTexture *Prev = nullptr;
PolyHardwareTexture *Next = nullptr;
std::unique_ptr<DCanvas> mCanvas;
std::unique_ptr<PolyDepthStencil> mDepthStencil;
};

View file

@ -0,0 +1,444 @@
/*
** Softpoly backend
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include "polyrenderer/backend/poly_renderstate.h"
#include "polyrenderer/backend/poly_framebuffer.h"
#include "polyrenderer/backend/poly_hwtexture.h"
#include "templates.h"
#include "hw_skydome.h"
#include "hw_viewpointuniforms.h"
#include "hw_lightbuffer.h"
#include "hw_cvars.h"
#include "hw_clock.h"
#include "flatvertices.h"
#include "hwrenderer/data/hw_viewpointbuffer.h"
#include "hwrenderer/data/shaderuniforms.h"
static PolyDrawMode dtToDrawMode[] =
{
PolyDrawMode::Points,
PolyDrawMode::Lines,
PolyDrawMode::Triangles,
PolyDrawMode::TriangleFan,
PolyDrawMode::TriangleStrip,
};
PolyRenderState::PolyRenderState()
{
mIdentityMatrix.loadIdentity();
Reset();
}
void PolyRenderState::ClearScreen()
{
screen->mViewpoints->Set2D(*this, SCREENWIDTH, SCREENHEIGHT);
SetColor(0, 0, 0);
Draw(DT_TriangleStrip, FFlatVertexBuffer::FULLSCREEN_INDEX, 4, true);
}
void PolyRenderState::Draw(int dt, int index, int count, bool apply)
{
if (apply || mNeedApply)
Apply();
mDrawCommands->Draw(index, count, dtToDrawMode[dt]);
}
void PolyRenderState::DrawIndexed(int dt, int index, int count, bool apply)
{
if (apply || mNeedApply)
Apply();
mDrawCommands->DrawIndexed(index, count, dtToDrawMode[dt]);
}
bool PolyRenderState::SetDepthClamp(bool on)
{
bool lastValue = mDepthClamp;
mDepthClamp = on;
mNeedApply = true;
return lastValue;
}
void PolyRenderState::SetDepthMask(bool on)
{
mDepthMask = on;
mNeedApply = true;
}
void PolyRenderState::SetDepthFunc(int func)
{
mDepthFunc = func;
mNeedApply = true;
}
void PolyRenderState::SetDepthRange(float min, float max)
{
mDepthRangeMin = min;
mDepthRangeMax = max;
mNeedApply = true;
}
void PolyRenderState::SetColorMask(bool r, bool g, bool b, bool a)
{
mColorMask[0] = r;
mColorMask[1] = g;
mColorMask[2] = b;
mColorMask[3] = a;
mNeedApply = true;
}
void PolyRenderState::SetStencil(int offs, int op, int flags)
{
mStencilValue = screen->stencilValue + offs;
mStencilOp = op;
mNeedApply = true;
if (flags != -1)
{
bool cmon = !(flags & SF_ColorMaskOff);
SetColorMask(cmon, cmon, cmon, cmon); // don't write to the graphics buffer
SetDepthMask(!(flags & SF_DepthMaskOff));
}
}
void PolyRenderState::SetCulling(int mode)
{
mCulling = mode;
mNeedApply = true;
}
void PolyRenderState::EnableClipDistance(int num, bool state)
{
}
void PolyRenderState::Clear(int targets)
{
if (mNeedApply)
Apply();
//if (targets & CT_Color)
// mDrawCommands->ClearColor();
if (targets & CT_Depth)
mDrawCommands->ClearDepth(65535.0f);
if (targets & CT_Stencil)
mDrawCommands->ClearStencil(0);
}
void PolyRenderState::EnableStencil(bool on)
{
mStencilEnabled = on;
mNeedApply = true;
}
void PolyRenderState::SetScissor(int x, int y, int w, int h)
{
if (w < 0)
{
x = 0;
y = 0;
w = mRenderTarget.Canvas->GetWidth();
h = mRenderTarget.Canvas->GetHeight();
}
mScissor.x = x;
mScissor.y = mRenderTarget.Canvas->GetHeight() - y - h;
mScissor.width = w;
mScissor.height = h;
mNeedApply = true;
}
void PolyRenderState::SetViewport(int x, int y, int w, int h)
{
auto fb = GetPolyFrameBuffer();
if (w < 0)
{
x = 0;
y = 0;
w = mRenderTarget.Canvas->GetWidth();
h = mRenderTarget.Canvas->GetHeight();
}
mViewport.x = x;
mViewport.y = mRenderTarget.Canvas->GetHeight() - y - h;
mViewport.width = w;
mViewport.height = h;
mNeedApply = true;
}
void PolyRenderState::EnableDepthTest(bool on)
{
mDepthTest = on;
mNeedApply = true;
}
void PolyRenderState::EnableMultisampling(bool on)
{
}
void PolyRenderState::EnableLineSmooth(bool on)
{
}
void PolyRenderState::EnableDrawBuffers(int count, bool apply)
{
}
void PolyRenderState::SetColormapShader(bool enable)
{
mNeedApply = true;
mColormapShader = enable;
}
void PolyRenderState::EndRenderPass()
{
mDrawCommands = nullptr;
mNeedApply = true;
mFirstMatrixApply = true;
}
void PolyRenderState::Apply()
{
drawcalls.Clock();
if (!mDrawCommands)
{
mDrawCommands = GetPolyFrameBuffer()->GetDrawCommands();
}
if (mNeedApply)
{
mDrawCommands->SetViewport(mViewport.x, mViewport.y, mViewport.width, mViewport.height, mRenderTarget.Canvas, mRenderTarget.DepthStencil, mRenderTarget.TopDown);
mDrawCommands->SetScissor(mScissor.x, mScissor.y, mScissor.width, mScissor.height);
mDrawCommands->SetViewpointUniforms(mViewpointUniforms);
mDrawCommands->SetDepthClamp(mDepthClamp);
mDrawCommands->SetDepthMask(mDepthTest && mDepthMask);
mDrawCommands->SetDepthFunc(mDepthTest ? mDepthFunc : DF_Always);
mDrawCommands->SetDepthRange(mDepthRangeMin, mDepthRangeMax);
mDrawCommands->SetStencil(mStencilValue, mStencilOp);
mDrawCommands->EnableStencil(mStencilEnabled);
mDrawCommands->SetCulling(mCulling);
mDrawCommands->SetColorMask(mColorMask[0], mColorMask[1], mColorMask[2], mColorMask[3]);
mNeedApply = false;
}
int fogset = 0;
if (mFogEnabled)
{
if (mFogEnabled == 2)
{
fogset = -3; // 2D rendering with 'foggy' overlay.
}
else if ((GetFogColor() & 0xffffff) == 0)
{
fogset = gl_fogmode;
}
else
{
fogset = -gl_fogmode;
}
}
ApplyMaterial();
if (mVertexBuffer) mDrawCommands->SetVertexBuffer(mVertexBuffer->Memory());
if (mIndexBuffer) mDrawCommands->SetIndexBuffer(mIndexBuffer->Memory());
mDrawCommands->SetInputAssembly(static_cast<PolyVertexBuffer*>(mVertexBuffer)->VertexFormat);
mDrawCommands->SetRenderStyle(mRenderStyle);
if (mColormapShader)
{
mDrawCommands->SetShader(EFF_NONE, 0, false, true);
}
else if (mSpecialEffect > EFF_NONE)
{
mDrawCommands->SetShader(mSpecialEffect, 0, false, false);
}
else
{
int effectState = mMaterial.mOverrideShader >= 0 ? mMaterial.mOverrideShader : (mMaterial.mMaterial ? mMaterial.mMaterial->GetShaderIndex() : 0);
mDrawCommands->SetShader(EFF_NONE, mTextureEnabled ? effectState : SHADER_NoTexture, mAlphaThreshold >= 0.f, false);
}
if (mMaterial.mMaterial && mMaterial.mMaterial->Source())
mStreamData.timer = static_cast<float>((double)(screen->FrameTime - firstFrame) * (double)mMaterial.mMaterial->Source()->GetShaderSpeed() / 1000.);
else
mStreamData.timer = 0.0f;
PolyPushConstants constants;
constants.uFogEnabled = fogset;
constants.uTextureMode = (mTextureMode == TM_NORMAL && mTempTM == TM_OPAQUE ? TM_OPAQUE : mTextureMode);
constants.uLightDist = mLightParms[0];
constants.uLightFactor = mLightParms[1];
constants.uFogDensity = mLightParms[2];
constants.uLightLevel = mLightParms[3];
constants.uAlphaThreshold = mAlphaThreshold;
constants.uClipSplit = { mClipSplit[0], mClipSplit[1] };
constants.uLightIndex = mLightIndex;
mDrawCommands->PushStreamData(mStreamData, constants);
ApplyMatrices();
if (mBias.mChanged)
{
mDrawCommands->SetDepthBias(mBias.mUnits, mBias.mFactor);
mBias.mChanged = false;
}
drawcalls.Unclock();
}
void PolyRenderState::ApplyMaterial()
{
if (mMaterial.mChanged && mMaterial.mMaterial)
{
mTempTM = mMaterial.mMaterial->Source()->isHardwareCanvas() ? TM_OPAQUE : TM_NORMAL;
if (mMaterial.mMaterial->Source()->isHardwareCanvas()) static_cast<FCanvasTexture*>(mMaterial.mMaterial->Source()->GetTexture())->NeedUpdate();
MaterialLayerInfo* layer;
auto base = static_cast<PolyHardwareTexture*>(mMaterial.mMaterial->GetLayer(0, mMaterial.mTranslation, &layer));
if (base)
{
DCanvas *texcanvas = base->GetImage(layer->layerTexture, mMaterial.mTranslation, layer->scaleFlags);
mDrawCommands->SetTexture(0, texcanvas->GetPixels(), texcanvas->GetWidth(), texcanvas->GetHeight(), texcanvas->IsBgra());
int numLayers = mMaterial.mMaterial->NumLayers();
for (int i = 1; i < numLayers; i++)
{
auto systex = static_cast<PolyHardwareTexture*>(mMaterial.mMaterial->GetLayer(i, 0, &layer));
texcanvas = systex->GetImage(layer->layerTexture, 0, layer->scaleFlags);
mDrawCommands->SetTexture(i, texcanvas->GetPixels(), texcanvas->GetWidth(), texcanvas->GetHeight(), texcanvas->IsBgra());
}
}
mMaterial.mChanged = false;
}
}
template<typename T>
static void BufferedSet(bool &modified, T &dst, const T &src)
{
if (dst == src)
return;
dst = src;
modified = true;
}
static void BufferedSet(bool &modified, VSMatrix &dst, const VSMatrix &src)
{
if (memcmp(dst.get(), src.get(), sizeof(FLOATTYPE) * 16) == 0)
return;
dst = src;
modified = true;
}
void PolyRenderState::ApplyMatrices()
{
bool modified = mFirstMatrixApply;
if (mTextureMatrixEnabled)
{
BufferedSet(modified, mMatrices.TextureMatrix, mTextureMatrix);
}
else
{
BufferedSet(modified, mMatrices.TextureMatrix, mIdentityMatrix);
}
if (mModelMatrixEnabled)
{
BufferedSet(modified, mMatrices.ModelMatrix, mModelMatrix);
if (modified)
mMatrices.NormalModelMatrix.computeNormalMatrix(mModelMatrix);
}
else
{
BufferedSet(modified, mMatrices.ModelMatrix, mIdentityMatrix);
BufferedSet(modified, mMatrices.NormalModelMatrix, mIdentityMatrix);
}
if (modified)
{
mFirstMatrixApply = false;
mDrawCommands->PushMatrices(mMatrices.ModelMatrix, mMatrices.NormalModelMatrix, mMatrices.TextureMatrix);
}
}
void PolyRenderState::SetRenderTarget(DCanvas *canvas, PolyDepthStencil *depthStencil, bool topdown)
{
mRenderTarget.Canvas = canvas;
mRenderTarget.DepthStencil = depthStencil;
mRenderTarget.TopDown = topdown;
}
void PolyRenderState::Bind(PolyDataBuffer *buffer, uint32_t offset, uint32_t length)
{
if (buffer->bindingpoint == VIEWPOINT_BINDINGPOINT)
{
mViewpointUniforms = reinterpret_cast<HWViewpointUniforms*>(static_cast<uint8_t*>(buffer->Memory()) + offset);
mNeedApply = true;
}
}
PolyVertexInputAssembly *PolyRenderState::GetVertexFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs)
{
for (size_t i = 0; i < mVertexFormats.size(); i++)
{
auto f = mVertexFormats[i].get();
if (f->Attrs.size() == (size_t)numAttributes && f->NumBindingPoints == numBindingPoints && f->Stride == stride)
{
bool matches = true;
for (int j = 0; j < numAttributes; j++)
{
if (memcmp(&f->Attrs[j], &attrs[j], sizeof(FVertexBufferAttribute)) != 0)
{
matches = false;
break;
}
}
if (matches)
return f;
}
}
auto fmt = std::make_unique<PolyVertexInputAssembly>();
fmt->NumBindingPoints = numBindingPoints;
fmt->Stride = stride;
fmt->UseVertexData = 0;
for (int j = 0; j < numAttributes; j++)
{
if (attrs[j].location == VATTR_COLOR)
fmt->UseVertexData |= 1;
else if (attrs[j].location == VATTR_NORMAL)
fmt->UseVertexData |= 2;
fmt->Attrs.push_back(attrs[j]);
}
for (int j = 0; j < numAttributes; j++)
{
fmt->mOffsets[attrs[j].location] = attrs[j].offset;
}
fmt->mStride = stride;
mVertexFormats.push_back(std::move(fmt));
return mVertexFormats.back().get();
}

View file

@ -0,0 +1,99 @@
#pragma once
#include "polyrenderer/backend/poly_buffers.h"
#include "poly_triangle.h"
#include "name.h"
#include "hw_renderstate.h"
#include "hw_material.h"
struct HWViewpointUniforms;
class PolyRenderState final : public FRenderState
{
public:
PolyRenderState();
// Draw commands
void ClearScreen() override;
void Draw(int dt, int index, int count, bool apply = true) override;
void DrawIndexed(int dt, int index, int count, bool apply = true) override;
// Immediate render state change commands. These only change infrequently and should not clutter the render state.
bool SetDepthClamp(bool on) override;
void SetDepthMask(bool on) override;
void SetDepthFunc(int func) override;
void SetDepthRange(float min, float max) override;
void SetColorMask(bool r, bool g, bool b, bool a) override;
void SetStencil(int offs, int op, int flags = -1) override;
void SetCulling(int mode) override;
void EnableClipDistance(int num, bool state) override;
void Clear(int targets) override;
void EnableStencil(bool on) override;
void SetScissor(int x, int y, int w, int h) override;
void SetViewport(int x, int y, int w, int h) override;
void EnableDepthTest(bool on) override;
void EnableMultisampling(bool on) override;
void EnableLineSmooth(bool on) override;
void EnableDrawBuffers(int count, bool apply) override;
void SetRenderTarget(DCanvas *canvas, PolyDepthStencil *depthStencil, bool topdown);
void Bind(PolyDataBuffer *buffer, uint32_t offset, uint32_t length);
PolyVertexInputAssembly *GetVertexFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs);
void EndRenderPass();
void SetColormapShader(bool enable);
private:
void Apply();
void ApplyMaterial();
void ApplyMatrices();
struct Matrices
{
VSMatrix ModelMatrix;
VSMatrix NormalModelMatrix;
VSMatrix TextureMatrix;
} mMatrices;
VSMatrix mIdentityMatrix;
bool mFirstMatrixApply = true;
HWViewpointUniforms *mViewpointUniforms = nullptr;
std::vector<std::unique_ptr<PolyVertexInputAssembly>> mVertexFormats;
bool mDepthClamp = true;
int mTempTM = TM_NORMAL;
struct RenderTarget
{
DCanvas *Canvas = nullptr;
PolyDepthStencil *DepthStencil = nullptr;
bool TopDown = true;
} mRenderTarget;
struct Rect
{
int x = 0;
int y = 0;
int width = 0;
int height = 0;
} mScissor, mViewport;
bool mNeedApply = true;
bool mDepthTest = false;
bool mDepthMask = false;
int mDepthFunc = DF_Always;
float mDepthRangeMin = 0.0f;
float mDepthRangeMax = 1.0f;
bool mStencilEnabled = false;
int mStencilValue = 0;
int mStencilOp = SOP_Keep;
int mCulling = Cull_None;
bool mColorMask[4] = { true, true, true, true };
bool mColormapShader = false;
PolyCommandBuffer* mDrawCommands = nullptr;
};