mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-11 15:22:15 +00:00
- base the viewpoint buffer on IDataBuffer.
This commit is contained in:
parent
3b26e64404
commit
b51cc8b115
9 changed files with 61 additions and 118 deletions
|
@ -29,17 +29,18 @@
|
||||||
#include "gl_load/gl_interface.h"
|
#include "gl_load/gl_interface.h"
|
||||||
#include "hwrenderer/data/shaderuniforms.h"
|
#include "hwrenderer/data/shaderuniforms.h"
|
||||||
#include "hwrenderer/scene/hw_viewpointuniforms.h"
|
#include "hwrenderer/scene/hw_viewpointuniforms.h"
|
||||||
|
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||||
#include "gl_viewpointbuffer.h"
|
#include "gl_viewpointbuffer.h"
|
||||||
|
|
||||||
static const int INITIAL_BUFFER_SIZE = 100; // 100 viewpoints per frame should nearly always be enough
|
static const int INITIAL_BUFFER_SIZE = 100; // 100 viewpoints per frame should nearly always be enough
|
||||||
|
|
||||||
GLViewpointBuffer::GLViewpointBuffer()
|
GLViewpointBuffer::GLViewpointBuffer()
|
||||||
{
|
{
|
||||||
mPersistent = screen->BuffersArePersistent();
|
|
||||||
mBufferSize = INITIAL_BUFFER_SIZE;
|
mBufferSize = INITIAL_BUFFER_SIZE;
|
||||||
mBlockAlign = ((sizeof(HWViewpointUniforms) / gl.uniformblockalignment) + 1) * gl.uniformblockalignment;
|
mBlockAlign = ((sizeof(HWViewpointUniforms) / screen->uniformblockalignment) + 1) * screen->uniformblockalignment;
|
||||||
mByteSize = mBufferSize * mBlockAlign;
|
mByteSize = mBufferSize * mBlockAlign;
|
||||||
Allocate();
|
mBuffer = screen->CreateDataBuffer(VIEWPOINT_BINDINGPOINT, false);
|
||||||
|
mBuffer->SetData(mByteSize, nullptr, false);
|
||||||
Clear();
|
Clear();
|
||||||
mLastMappedIndex = UINT_MAX;
|
mLastMappedIndex = UINT_MAX;
|
||||||
mClipPlaneInfo.Push(0);
|
mClipPlaneInfo.Push(0);
|
||||||
|
@ -47,98 +48,32 @@ GLViewpointBuffer::GLViewpointBuffer()
|
||||||
|
|
||||||
GLViewpointBuffer::~GLViewpointBuffer()
|
GLViewpointBuffer::~GLViewpointBuffer()
|
||||||
{
|
{
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
delete mBuffer;
|
||||||
glDeleteBuffers(1, &mBufferId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLViewpointBuffer::Allocate()
|
|
||||||
{
|
|
||||||
glGenBuffers(1, &mBufferId);
|
|
||||||
glBindBufferBase(GL_UNIFORM_BUFFER, VIEWPOINT_BINDINGPOINT, mBufferId);
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, mBufferId); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should.
|
|
||||||
if (mPersistent)
|
|
||||||
{
|
|
||||||
glBufferStorage(GL_UNIFORM_BUFFER, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
|
||||||
mBufferPointer = glMapBufferRange(GL_UNIFORM_BUFFER, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
glBufferData(GL_UNIFORM_BUFFER, mByteSize, NULL, GL_STATIC_DRAW);
|
|
||||||
mBufferPointer = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLViewpointBuffer::CheckSize()
|
void GLViewpointBuffer::CheckSize()
|
||||||
{
|
{
|
||||||
if (mUploadIndex >= mBufferSize)
|
if (mUploadIndex >= mBufferSize)
|
||||||
{
|
{
|
||||||
// reallocate the buffer with twice the size
|
|
||||||
unsigned int oldbuffer = mBufferId;
|
|
||||||
|
|
||||||
mBufferSize *= 2;
|
mBufferSize *= 2;
|
||||||
mByteSize *= 2;
|
mByteSize *= 2;
|
||||||
|
mBuffer->Resize(mByteSize);
|
||||||
// first unmap the old buffer
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, mBufferId);
|
|
||||||
glUnmapBuffer(GL_UNIFORM_BUFFER);
|
|
||||||
|
|
||||||
Allocate();
|
|
||||||
glBindBuffer(GL_COPY_READ_BUFFER, oldbuffer);
|
|
||||||
|
|
||||||
// copy contents and delete the old buffer.
|
|
||||||
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_UNIFORM_BUFFER, 0, 0, mByteSize / 2); // old size is half of the current one.
|
|
||||||
glBindBuffer(GL_COPY_READ_BUFFER, 0);
|
|
||||||
glDeleteBuffers(1, &oldbuffer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLViewpointBuffer::Map()
|
int GLViewpointBuffer::Bind(HWDrawInfo *di, unsigned int index)
|
||||||
{
|
|
||||||
if (!mPersistent)
|
|
||||||
{
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, mBufferId);
|
|
||||||
mBufferPointer = (float*)glMapBufferRange(GL_UNIFORM_BUFFER, 0, mByteSize, GL_MAP_WRITE_BIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLViewpointBuffer::Unmap()
|
|
||||||
{
|
|
||||||
if (!mPersistent)
|
|
||||||
{
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, mBufferId);
|
|
||||||
glUnmapBuffer(GL_UNIFORM_BUFFER);
|
|
||||||
mBufferPointer = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int GLViewpointBuffer::Bind(unsigned int index)
|
|
||||||
{
|
{
|
||||||
if (index != mLastMappedIndex)
|
if (index != mLastMappedIndex)
|
||||||
{
|
{
|
||||||
mLastMappedIndex = index;
|
mLastMappedIndex = index;
|
||||||
glBindBufferRange(GL_UNIFORM_BUFFER, VIEWPOINT_BINDINGPOINT, mBufferId, index * mBlockAlign, mBlockAlign);
|
mBuffer->BindRange(index * mBlockAlign, mBlockAlign);
|
||||||
|
di->EnableClipDistance(0, mClipPlaneInfo[index]);
|
||||||
// Update the viewpoint-related clip plane setting.
|
|
||||||
if (!(gl.flags & RFL_NO_CLIP_PLANES))
|
|
||||||
{
|
|
||||||
if (mClipPlaneInfo[index])
|
|
||||||
{
|
|
||||||
glEnable(GL_CLIP_DISTANCE0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
glDisable(GL_CLIP_DISTANCE0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLViewpointBuffer::Set2D(int width, int height)
|
void GLViewpointBuffer::Set2D(HWDrawInfo *di, int width, int height)
|
||||||
{
|
{
|
||||||
if (width != m2DWidth || height != m2DHeight)
|
if (width != m2DWidth || height != m2DHeight)
|
||||||
{
|
{
|
||||||
|
@ -146,25 +81,25 @@ void GLViewpointBuffer::Set2D(int width, int height)
|
||||||
matrices.SetDefaults();
|
matrices.SetDefaults();
|
||||||
matrices.mProjectionMatrix.ortho(0, width, height, 0, -1.0f, 1.0f);
|
matrices.mProjectionMatrix.ortho(0, width, height, 0, -1.0f, 1.0f);
|
||||||
matrices.CalcDependencies();
|
matrices.CalcDependencies();
|
||||||
Map();
|
mBuffer->Map();
|
||||||
memcpy(mBufferPointer, &matrices, sizeof(matrices));
|
memcpy(mBuffer->Memory(), &matrices, sizeof(matrices));
|
||||||
Unmap();
|
mBuffer->Unmap();
|
||||||
m2DWidth = width;
|
m2DWidth = width;
|
||||||
m2DHeight = height;
|
m2DHeight = height;
|
||||||
mLastMappedIndex = -1;
|
mLastMappedIndex = -1;
|
||||||
}
|
}
|
||||||
Bind(0);
|
Bind(di, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GLViewpointBuffer::SetViewpoint(HWViewpointUniforms *vp)
|
int GLViewpointBuffer::SetViewpoint(HWDrawInfo *di, HWViewpointUniforms *vp)
|
||||||
{
|
{
|
||||||
CheckSize();
|
CheckSize();
|
||||||
Map();
|
mBuffer->Map();
|
||||||
memcpy(((char*)mBufferPointer) + mUploadIndex * mBlockAlign, vp, sizeof(*vp));
|
memcpy(((char*)mBuffer->Memory()) + mUploadIndex * mBlockAlign, vp, sizeof(*vp));
|
||||||
Unmap();
|
mBuffer->Unmap();
|
||||||
|
|
||||||
mClipPlaneInfo.Push(vp->mClipHeightDirection != 0.f || vp->mClipLine.X > -10000000.0f);
|
mClipPlaneInfo.Push(vp->mClipHeightDirection != 0.f || vp->mClipLine.X > -10000000.0f);
|
||||||
return Bind(mUploadIndex++);
|
return Bind(di, mUploadIndex++);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLViewpointBuffer::Clear()
|
void GLViewpointBuffer::Clear()
|
||||||
|
|
|
@ -1,37 +1,35 @@
|
||||||
|
|
||||||
#include "tarray.h"
|
#include "tarray.h"
|
||||||
|
#include "hwrenderer/data/buffers.h"
|
||||||
|
|
||||||
struct HWViewpointUniforms;
|
struct HWViewpointUniforms;
|
||||||
|
struct HWDrawInfo;
|
||||||
|
|
||||||
class GLViewpointBuffer
|
class GLViewpointBuffer
|
||||||
{
|
{
|
||||||
unsigned int mBufferId;
|
IDataBuffer *mBuffer;
|
||||||
|
|
||||||
unsigned int mBufferSize;
|
unsigned int mBufferSize;
|
||||||
unsigned int mBlockAlign;
|
unsigned int mBlockAlign;
|
||||||
unsigned int mUploadIndex;
|
unsigned int mUploadIndex;
|
||||||
unsigned int mLastMappedIndex;
|
unsigned int mLastMappedIndex;
|
||||||
unsigned int mByteSize;
|
unsigned int mByteSize;
|
||||||
void * mBufferPointer;
|
|
||||||
TArray<bool> mClipPlaneInfo;
|
TArray<bool> mClipPlaneInfo;
|
||||||
bool mPersistent;
|
|
||||||
|
|
||||||
unsigned int m2DWidth = ~0u, m2DHeight = ~0u;
|
unsigned int m2DWidth = ~0u, m2DHeight = ~0u;
|
||||||
|
|
||||||
unsigned int mBlockSize;
|
unsigned int mBlockSize;
|
||||||
|
|
||||||
void CheckSize();
|
void CheckSize();
|
||||||
void Allocate();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
GLViewpointBuffer();
|
GLViewpointBuffer();
|
||||||
~GLViewpointBuffer();
|
~GLViewpointBuffer();
|
||||||
void Clear();
|
void Clear();
|
||||||
void Map();
|
int Bind(HWDrawInfo *di, unsigned int index);
|
||||||
void Unmap();
|
void Set2D(HWDrawInfo *di, int width, int height);
|
||||||
int Bind(unsigned int index);
|
int SetViewpoint(HWDrawInfo *di, HWViewpointUniforms *vp);
|
||||||
void Set2D(int width, int height);
|
|
||||||
int SetViewpoint(HWViewpointUniforms *vp);
|
|
||||||
unsigned int GetBlockSize() const { return mBlockSize; }
|
unsigned int GetBlockSize() const { return mBlockSize; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -418,9 +418,11 @@ void FGLRenderer::Draw2D(F2DDrawer *drawer)
|
||||||
FGLDebug::PushGroup("Draw2D");
|
FGLDebug::PushGroup("Draw2D");
|
||||||
if (VRMode::GetVRMode(true)->mEyeCount == 1)
|
if (VRMode::GetVRMode(true)->mEyeCount == 1)
|
||||||
mBuffers->BindCurrentFB();
|
mBuffers->BindCurrentFB();
|
||||||
|
|
||||||
|
FDrawInfo di; // For access to the virtual interface. This should be placed elsewhere...
|
||||||
const auto &mScreenViewport = screen->mScreenViewport;
|
const auto &mScreenViewport = screen->mScreenViewport;
|
||||||
glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
|
glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
|
||||||
GLRenderer->mViewpoints->Set2D(screen->GetWidth(), screen->GetHeight());
|
GLRenderer->mViewpoints->Set2D(&di, screen->GetWidth(), screen->GetHeight());
|
||||||
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
|
|
@ -263,7 +263,7 @@ void FDrawInfo::RenderPortal(HWPortal *p, bool usestencil)
|
||||||
gp->DrawContents(new_di, gl_RenderState);
|
gp->DrawContents(new_di, gl_RenderState);
|
||||||
new_di->EndDrawInfo();
|
new_di->EndDrawInfo();
|
||||||
GLRenderer->mVBO->Bind(gl_RenderState);
|
GLRenderer->mVBO->Bind(gl_RenderState);
|
||||||
GLRenderer->mViewpoints->Bind(vpIndex);
|
GLRenderer->mViewpoints->Bind(this, vpIndex);
|
||||||
gp->RemoveStencil(this, gl_RenderState, usestencil);
|
gp->RemoveStencil(this, gl_RenderState, usestencil);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -320,6 +320,21 @@ void FDrawInfo::SetCulling(int mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FDrawInfo::EnableClipDistance(int num, bool state)
|
||||||
|
{
|
||||||
|
// Update the viewpoint-related clip plane setting.
|
||||||
|
if (!(gl.flags & RFL_NO_CLIP_PLANES))
|
||||||
|
{
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
glEnable(GL_CLIP_DISTANCE0+num);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glDisable(GL_CLIP_DISTANCE0+num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
|
@ -330,7 +345,7 @@ void FDrawInfo::ClearScreen()
|
||||||
{
|
{
|
||||||
bool multi = !!glIsEnabled(GL_MULTISAMPLE);
|
bool multi = !!glIsEnabled(GL_MULTISAMPLE);
|
||||||
|
|
||||||
GLRenderer->mViewpoints->Set2D(SCREENWIDTH, SCREENHEIGHT);
|
GLRenderer->mViewpoints->Set2D(this, SCREENWIDTH, SCREENHEIGHT);
|
||||||
gl_RenderState.SetColor(0, 0, 0);
|
gl_RenderState.SetColor(0, 0, 0);
|
||||||
gl_RenderState.Apply();
|
gl_RenderState.Apply();
|
||||||
|
|
||||||
|
|
|
@ -54,12 +54,12 @@ struct FDrawInfo : public HWDrawInfo
|
||||||
void EnableDrawBufferAttachments(bool on) override;
|
void EnableDrawBufferAttachments(bool on) override;
|
||||||
void SetStencil(int offs, int op, int flags) override;
|
void SetStencil(int offs, int op, int flags) override;
|
||||||
void SetCulling(int mode) override;
|
void SetCulling(int mode) override;
|
||||||
|
void EnableClipDistance(int num, bool state) override;
|
||||||
|
|
||||||
void StartScene();
|
void StartScene();
|
||||||
|
|
||||||
void DrawSorted(int listindex);
|
void DrawSorted(int listindex);
|
||||||
|
|
||||||
// These two may be moved to the API independent part of the renderer later.
|
|
||||||
void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) override;
|
void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub) override;
|
||||||
|
|
||||||
void CreateScene();
|
void CreateScene();
|
||||||
|
@ -74,16 +74,5 @@ struct FDrawInfo : public HWDrawInfo
|
||||||
|
|
||||||
static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms);
|
static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms);
|
||||||
FDrawInfo *EndDrawInfo();
|
FDrawInfo *EndDrawInfo();
|
||||||
|
|
||||||
void SetColor(int light, int rellight, const FColormap &cm, float alpha, bool weapon = false)
|
|
||||||
{
|
|
||||||
gl_RenderState.SetColor(light, rellight, isFullbrightScene(), cm, alpha, weapon);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetFog(int lightlevel, int rellight, const FColormap *cmap, bool isadditive)
|
|
||||||
{
|
|
||||||
gl_RenderState.SetFog(lightlevel, rellight, isFullbrightScene(), cmap, isadditive);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -78,7 +78,7 @@ EXTERN_CVAR (Bool, r_drawvoxels)
|
||||||
void FDrawInfo::ApplyVPUniforms()
|
void FDrawInfo::ApplyVPUniforms()
|
||||||
{
|
{
|
||||||
VPUniforms.CalcDependencies();
|
VPUniforms.CalcDependencies();
|
||||||
vpIndex = GLRenderer->mViewpoints->SetViewpoint(&VPUniforms);
|
vpIndex = GLRenderer->mViewpoints->SetViewpoint(this, &VPUniforms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ void FDrawInfo::DrawScene(int drawmode)
|
||||||
GLRenderer->mBuffers->BindSceneFB(true);
|
GLRenderer->mBuffers->BindSceneFB(true);
|
||||||
gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount());
|
gl_RenderState.EnableDrawBuffers(gl_RenderState.GetPassDrawBufferCount());
|
||||||
gl_RenderState.Apply();
|
gl_RenderState.Apply();
|
||||||
GLRenderer->mViewpoints->Bind(vpIndex);
|
GLRenderer->mViewpoints->Bind(this, vpIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle all portals after rendering the opaque objects but before
|
// Handle all portals after rendering the opaque objects but before
|
||||||
|
@ -329,7 +329,7 @@ void FDrawInfo::DrawEndScene2D(sector_t * viewsector)
|
||||||
HWViewpointUniforms vp = VPUniforms;
|
HWViewpointUniforms vp = VPUniforms;
|
||||||
vp.mViewMatrix.loadIdentity();
|
vp.mViewMatrix.loadIdentity();
|
||||||
vp.mProjectionMatrix = vrmode->GetHUDSpriteProjection();
|
vp.mProjectionMatrix = vrmode->GetHUDSpriteProjection();
|
||||||
GLRenderer->mViewpoints->SetViewpoint(&vp);
|
GLRenderer->mViewpoints->SetViewpoint(this, &vp);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
glDisable(GL_MULTISAMPLE);
|
glDisable(GL_MULTISAMPLE);
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ void OpenGLFrameBuffer::InitializeState()
|
||||||
// Move some state to the framebuffer object for easier access.
|
// Move some state to the framebuffer object for easier access.
|
||||||
hwcaps = gl.flags;
|
hwcaps = gl.flags;
|
||||||
glslversion = gl.glslversion;
|
glslversion = gl.glslversion;
|
||||||
|
uniformblockalignment = gl.uniformblockalignment;
|
||||||
|
|
||||||
if (first)
|
if (first)
|
||||||
{
|
{
|
||||||
|
|
|
@ -362,6 +362,7 @@ public:
|
||||||
virtual void EnableDrawBufferAttachments(bool on) = 0;
|
virtual void EnableDrawBufferAttachments(bool on) = 0;
|
||||||
virtual void SetStencil(int offs, int op, int flags) = 0;
|
virtual void SetStencil(int offs, int op, int flags) = 0;
|
||||||
virtual void SetCulling(int mode) = 0;
|
virtual void SetCulling(int mode) = 0;
|
||||||
|
virtual void EnableClipDistance(int num, bool state) = 0;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -365,11 +365,13 @@ protected:
|
||||||
PalEntry SourcePalette[256]; // This is where unpaletted textures get their palette from
|
PalEntry SourcePalette[256]; // This is where unpaletted textures get their palette from
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int hwcaps = 0;
|
// Hardware render state that needs to be exposed to the API independent part of the renderer. For ease of access this is stored in the base class.
|
||||||
|
int hwcaps = 0; // Capability flags
|
||||||
float glslversion = 0; // This is here so that the differences between old OpenGL and new OpenGL/Vulkan can be handled by platform independent code.
|
float glslversion = 0; // This is here so that the differences between old OpenGL and new OpenGL/Vulkan can be handled by platform independent code.
|
||||||
int instack[2] = { 0,0 }; // this is globally maintained state for portal recursion avoidance.
|
int instack[2] = { 0,0 }; // this is globally maintained state for portal recursion avoidance.
|
||||||
int stencilValue = 0; // Global stencil test value
|
int stencilValue = 0; // Global stencil test value
|
||||||
bool enable_quadbuffered = false;
|
bool enable_quadbuffered = false; // Quad-buffered stereo available?
|
||||||
|
unsigned int uniformblockalignment = 256; // Hardware dependent uniform buffer alignment.
|
||||||
FPortalSceneState *mPortalState; // global portal state.
|
FPortalSceneState *mPortalState; // global portal state.
|
||||||
FSkyVertexBuffer *mSkyData; // we need access to this in the device independent part, but cannot depend on how the renderer manages it internally.
|
FSkyVertexBuffer *mSkyData; // we need access to this in the device independent part, but cannot depend on how the renderer manages it internally.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue