Merge branch '12.0-development' into metal

This commit is contained in:
Alex Szpakowski
2020-12-22 22:37:17 -04:00
288 changed files with 15292 additions and 12550 deletions
+90 -120
View File
@@ -22,6 +22,7 @@
#include "common/Exception.h"
#include "graphics/vertex.h"
#include "Graphics.h"
#include <cstdlib>
#include <cstring>
@@ -80,22 +81,26 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
target = OpenGL::getGLBufferType(mapType);
try
if (usage == BUFFERUSAGE_STREAM)
ownsMemoryMap = true;
std::vector<uint8> emptydata;
if (settings.zeroInitialize && data == nullptr)
{
memoryMap = new char[size];
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
try
{
emptydata.resize(getSize());
data = emptydata.data();
}
catch (std::exception &)
{
data = nullptr;
}
}
if (data != nullptr)
memcpy(memoryMap, data, size);
if (!load(data != nullptr))
if (!load(data))
{
unloadVolatile();
delete[] memoryMap;
throw love::Exception("Could not create buffer (out of VRAM?)");
}
}
@@ -103,7 +108,8 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
Buffer::~Buffer()
{
unloadVolatile();
delete[] memoryMap;
if (memoryMap != nullptr && ownsMemoryMap)
free(memoryMap);
}
bool Buffer::loadVolatile()
@@ -111,7 +117,7 @@ bool Buffer::loadVolatile()
if (buffer != 0)
return true;
return load(true);
return load(nullptr);
}
void Buffer::unloadVolatile()
@@ -125,7 +131,7 @@ void Buffer::unloadVolatile()
texture = 0;
}
bool Buffer::load(bool restore)
bool Buffer::load(const void *initialdata)
{
while (glGetError() != GL_NO_ERROR)
/* Clear the error buffer. */;
@@ -133,11 +139,8 @@ bool Buffer::load(bool restore)
glGenBuffers(1, &buffer);
gl.bindBuffer(mapType, buffer);
// Copy the old buffer only if 'restore' was requested.
const GLvoid *src = restore ? memoryMap : nullptr;
// Note that if 'src' is '0', no data will be copied.
glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
// initialdata can be null.
glBufferData(target, (GLsizeiptr) getSize(), initialdata, OpenGL::getGLBufferUsage(getUsage()));
if (getTypeFlags() & TYPEFLAG_TEXEL)
{
@@ -150,137 +153,104 @@ bool Buffer::load(bool restore)
return (glGetError() == GL_NO_ERROR);
}
void *Buffer::map()
{
if (mapped)
return memoryMap;
mapped = true;
modifiedOffset = 0;
modifiedSize = 0;
isMappedDataModified = false;
return memoryMap;
}
void Buffer::unmapStatic(size_t offset, size_t size)
void *Buffer::map(MapType /*map*/, size_t offset, size_t size)
{
if (size == 0)
return;
return nullptr;
// Upload the mapped data to the buffer.
gl.bindBuffer(mapType, buffer);
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memoryMap + offset);
}
Range r(offset, size);
void Buffer::unmapStream()
{
GLenum glusage = OpenGL::getGLBufferUsage(getUsage());
if (!Range(0, getSize()).contains(r))
return nullptr;
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
gl.bindBuffer(mapType, buffer);
glBufferData(target, (GLsizeiptr) getSize(), nullptr, glusage);
char *data = nullptr;
#if LOVE_WINDOWS
// TODO: Verify that this codepath is a useful optimization.
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
glBufferData(target, (GLsizeiptr) getSize(), memoryMap, glusage);
if (ownsMemoryMap)
{
if (memoryMap == nullptr)
memoryMap = (char *) malloc(getSize());
data = memoryMap;
}
else
#endif
glBufferSubData(target, 0, (GLsizeiptr) getSize(), memoryMap);
{
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
data = (char *) gfx->getBufferMapMemory(size);
}
if (data != nullptr)
{
mapped = true;
mappedRange = r;
if (!ownsMemoryMap)
memoryMap = data;
}
return data;
}
void Buffer::unmap()
void Buffer::unmap(size_t usedoffset, size_t usedsize)
{
if (!mapped)
Range r(usedoffset, usedsize);
if (!mapped || !mappedRange.contains(r))
return;
mapped = false;
if ((mapFlags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
// Orphan optimization - see fill().
if (usage != BUFFERUSAGE_STATIC && mappedRange.first == 0 && mappedRange.getSize() == getSize())
{
if (!isMappedDataModified)
return;
modifiedOffset = std::min(modifiedOffset, getSize() - 1);
modifiedSize = std::min(modifiedSize, getSize() - modifiedOffset);
}
else
{
modifiedOffset = 0;
modifiedSize = getSize();
usedoffset = 0;
usedsize = getSize();
}
if (modifiedSize > 0)
char *data = memoryMap + (usedoffset - mappedRange.getOffset());
fill(usedoffset, usedsize, data);
if (!ownsMemoryMap)
{
switch (getUsage())
{
case BUFFERUSAGE_STATIC:
unmapStatic(modifiedOffset, modifiedSize);
break;
case BUFFERUSAGE_STREAM:
unmapStream();
break;
case BUFFERUSAGE_DYNAMIC:
default:
// It's probably more efficient to treat it like a streaming buffer if
// at least a third of its contents have been modified during the map().
if (modifiedSize >= getSize() / 3)
unmapStream();
else
unmapStatic(modifiedOffset, modifiedSize);
break;
}
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
gfx->releaseBufferMapMemory(memoryMap);
memoryMap = nullptr;
}
modifiedOffset = 0;
modifiedSize = 0;
}
void Buffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
{
if (!mapped || !(mapFlags & MAP_EXPLICIT_RANGE_MODIFY))
return;
if (!isMappedDataModified)
{
modifiedOffset = offset;
modifiedSize = modifiedsize;
isMappedDataModified = true;
return;
}
// We're being conservative right now by internally marking the whole range
// from the start of section a to the end of section b as modified if both
// a and b are marked as modified.
size_t oldrangeend = modifiedOffset + modifiedSize;
modifiedOffset = std::min(modifiedOffset, offset);
size_t newrangeend = std::max(offset + modifiedsize, oldrangeend);
modifiedSize = newrangeend - modifiedOffset;
}
void Buffer::fill(size_t offset, size_t size, const void *data)
{
memcpy(memoryMap + offset, data, size);
if (size == 0)
return;
if (mapped)
setMappedRangeModified(offset, size);
size_t buffersize = getSize();
if (!Range(0, buffersize).contains(Range(offset, size)))
return;
GLenum glusage = OpenGL::getGLBufferUsage(usage);
gl.bindBuffer(mapType, buffer);
if (usage != BUFFERUSAGE_STATIC && size == buffersize)
{
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
gl.bindBuffer(mapType, buffer);
glBufferData(target, (GLsizeiptr) buffersize, nullptr, glusage);
#if LOVE_WINDOWS
// TODO: Verify that this codepath is a useful optimization.
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
glBufferData(target, (GLsizeiptr) buffersize, data, glusage);
else
#endif
glBufferSubData(target, 0, (GLsizeiptr) buffersize, data);
}
else
{
gl.bindBuffer(mapType, buffer);
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data);
}
}
void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
{
other->fill(otheroffset, size, memoryMap + offset);
}
} // opengl
} // graphics
} // love
+6 -9
View File
@@ -22,6 +22,7 @@
// LOVE
#include "common/config.h"
#include "common/Range.h"
#include "graphics/Buffer.h"
#include "graphics/Volatile.h"
@@ -49,19 +50,16 @@ public:
bool loadVolatile() override;
void unloadVolatile() override;
void *map() override;
void unmap() override;
void setMappedRangeModified(size_t offset, size_t size) override;
void *map(MapType map, size_t offset, size_t size) override;
void unmap(size_t usedoffset, size_t usedsize) override;
void fill(size_t offset, size_t size, const void *data) override;
ptrdiff_t getHandle() const override { return buffer; };
ptrdiff_t getTexelBufferHandle() const override { return texture; };
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
private:
bool load(bool restore);
bool load(const void *initialdata);
void unmapStatic(size_t offset, size_t size);
void unmapStream();
@@ -77,10 +75,9 @@ private:
// A pointer to mapped memory.
char *memoryMap = nullptr;
bool ownsMemoryMap = false;
size_t modifiedOffset = 0;
size_t modifiedSize = 0;
bool isMappedDataModified = false;
Range mappedRange;
}; // Buffer
+166 -39
View File
@@ -107,11 +107,24 @@ love::graphics::Graphics *createInstance()
Graphics::Graphics()
: windowHasStencil(false)
, mainVAO(0)
, internalBackbufferFBO(0)
, requestedBackbufferMSAA(0)
, bufferMapMemory(nullptr)
, bufferMapMemorySize(2 * 1024 * 1024)
, defaultBuffers()
, supportedFormats()
{
gl = OpenGL();
try
{
bufferMapMemory = new char[bufferMapMemorySize];
}
catch (std::exception &)
{
// Handled in getBufferMapMemory.
}
auto window = getInstance<love::window::Window>(M_WINDOW);
if (window != nullptr)
@@ -121,21 +134,22 @@ Graphics::Graphics()
if (window->isOpen())
{
int w, h;
love::window::WindowSettings settings;
window->getWindow(w, h, settings);
love::window::WindowSettings s;
window->getWindow(w, h, s);
double dpiW = w;
double dpiH = h;
window->windowToDPICoords(&dpiW, &dpiH);
void *context = nullptr; // TODO
setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), settings.stencil, settings.depth);
setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), s.stencil, s.msaa);
}
}
}
Graphics::~Graphics()
{
delete[] bufferMapMemory;
}
const char *Graphics::getName() const
@@ -188,14 +202,94 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
// Set up the projection matrix
projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0, -10.0f, 10.0f);
}
updateBackbuffer(width, height, pixelwidth, pixelheight, requestedBackbufferMSAA);
}
bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int /*backbufferdepth*/)
void Graphics::updateBackbuffer(int width, int height, int /*pixelwidth*/, int pixelheight, int msaa)
{
bool useinternalbackbuffer = false;
if (msaa > 1)
useinternalbackbuffer = true;
// Our internal backbuffer code needs glBlitFramebuffer.
if (!(GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_ES_VERSION_3_0
|| GLAD_EXT_framebuffer_blit || GLAD_ANGLE_framebuffer_blit || GLAD_NV_framebuffer_blit))
{
if (!(msaa > 1 && GLAD_APPLE_framebuffer_multisample))
useinternalbackbuffer = false;
}
GLuint prevFBO = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
bool restoreFBO = prevFBO != getInternalBackbufferFBO();
if (useinternalbackbuffer)
{
Texture::Settings settings;
settings.width = width;
settings.height = height;
settings.dpiScale = (float)pixelheight / (float)height;
settings.msaa = msaa;
settings.renderTarget = true;
settings.readable.set(false);
settings.format = isGammaCorrect() ? PIXELFORMAT_RGBA8_UNORM_sRGB : PIXELFORMAT_RGBA8_UNORM;
internalBackbuffer.set(newTexture(settings), Acquire::NORETAIN);
settings.format = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
internalBackbufferDepthStencil.set(newTexture(settings), Acquire::NORETAIN);
RenderTargets rts;
rts.colors.push_back(internalBackbuffer.get());
rts.depthStencil.texture = internalBackbufferDepthStencil;
internalBackbufferFBO = bindCachedFBO(rts);
}
else
{
internalBackbuffer.set(nullptr);
internalBackbufferDepthStencil.set(nullptr);
internalBackbufferFBO = 0;
}
requestedBackbufferMSAA = msaa;
if (restoreFBO)
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, prevFBO);
}
GLuint Graphics::getInternalBackbufferFBO() const
{
if (internalBackbufferFBO != 0)
return internalBackbufferFBO;
else
return getSystemBackbufferFBO();
}
GLuint Graphics::getSystemBackbufferFBO() const
{
#ifdef LOVE_IOS
// Hack: iOS uses a custom FBO.
SDL_SysWMinfo info = {};
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
if (info.info.uikit.resolveFramebuffer != 0)
return info.info.uikit.resolveFramebuffer;
else
return info.info.uikit.framebuffer;
#else
return 0;
#endif
}
bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa)
{
this->width = width;
this->height = height;
this->windowHasStencil = backbufferstencil;
this->windowHasStencil = windowhasstencil;
this->requestedBackbufferMSAA = msaa;
// Okay, setup OpenGL.
gl.initContext();
@@ -211,8 +305,6 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
created = true;
initCapabilities();
setViewportSize(width, height, pixelwidth, pixelheight);
// Enable blending
gl.setEnableState(OpenGL::ENABLE_BLEND, true);
@@ -252,6 +344,8 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
setDebug(isDebugEnabled());
setViewportSize(width, height, pixelwidth, pixelheight);
if (batchedDrawState.vb[0] == nullptr)
{
// Initial sizes that should be good enough for most cases. It will
@@ -263,7 +357,7 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
if (capabilities.features[FEATURE_TEXEL_BUFFER] && defaultBuffers[BUFFERTYPE_TEXEL].get() == nullptr)
{
Buffer::Settings settings(Buffer::TYPEFLAG_TEXEL, 0, BUFFERUSAGE_STATIC);
Buffer::Settings settings(Buffer::TYPEFLAG_TEXEL, BUFFERUSAGE_STATIC);
std::vector<Buffer::DataDeclaration> format = {{"", DATAFORMAT_FLOAT_VEC4, 0}};
const float texel[] = {0.0f, 0.0f, 0.0f, 1.0f};
@@ -335,6 +429,9 @@ void Graphics::unSetMode()
flushBatchedDraws();
internalBackbuffer.set(nullptr);
internalBackbufferDepthStencil.set(nullptr);
// Unload all volatile objects. These must be reloaded after the display
// mode change.
Volatile::unloadAll();
@@ -552,7 +649,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
if (iswindow)
{
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
// The projection matrix is flipped compared to rendering to a texture,
// due to OpenGL considering (0,0) bottom-left instead of top-left.
@@ -594,6 +691,8 @@ void Graphics::endPass()
// Discard the depth/stencil buffer if we're using an internal cached one.
if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
discard({}, true);
else if (!rts.getFirstTarget().texture.get())
discard({}, true); // Backbuffer
// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
// don't have to worry about resolving to slices.
@@ -646,15 +745,12 @@ void Graphics::endPass()
}
}
// generateMipmaps can't be used for depth/stencil textures.
for (const auto &rt : rts.colors)
{
if (rt.texture->getMipmapsMode() == Texture::MIPMAPS_AUTO && rt.mipmap == 0)
rt.texture->generateMipmaps();
}
int dsmipmap = rts.depthStencil.mipmap;
if (depthstencil != nullptr && depthstencil->getMipmapsMode() == Texture::MIPMAPS_AUTO && dsmipmap == 0)
depthstencil->generateMipmaps();
}
void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth)
@@ -812,7 +908,7 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
attachments.reserve(colorbuffers.size());
// glDiscardFramebuffer uses different attachment enums for the default FBO.
if (!isRenderTargetActive() && gl.getDefaultFBO() == 0)
if (!isRenderTargetActive() && getInternalBackbufferFBO() == 0)
{
if (colorbuffers.size() > 0 && colorbuffers[0])
attachments.push_back(GL_COLOR);
@@ -879,7 +975,7 @@ void Graphics::cleanupRenderTexture(love::graphics::Texture *texture)
}
}
void Graphics::bindCachedFBO(const RenderTargets &targets)
GLuint Graphics::bindCachedFBO(const RenderTargets &targets)
{
GLuint fbo = framebufferObjects[targets];
@@ -962,6 +1058,8 @@ void Graphics::bindCachedFBO(const RenderTargets &targets)
framebufferObjects[targets] = fbo;
}
return fbo;
}
void Graphics::present(void *screenshotCallbackData)
@@ -977,13 +1075,34 @@ void Graphics::present(void *screenshotCallbackData)
flushBatchedDraws();
endPass();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO());
int w = getPixelWidth();
int h = getPixelHeight();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
// Copy internal backbuffer to system backbuffer. When MSAA is used this
// is a direct MSAA resolve.
if (internalBackbuffer.get())
{
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, getSystemBackbufferFBO());
// Discard system backbuffer to prevent it from copying its contents
// from VRAM to chip memory.
discard(OpenGL::FRAMEBUFFER_DRAW, {true}, true);
// updateBackbuffer checks for glBlitFramebuffer support.
if (GLAD_APPLE_framebuffer_multisample && internalBackbuffer->getMSAA() > 1)
glResolveMultisampleFramebufferAPPLE();
else
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Discarding the internal backbuffer directly after resolving it should
// eliminate any copy back to vram it might need to do.
discard(OpenGL::FRAMEBUFFER_READ, {true}, false);
}
if (!pendingScreenshotCallbacks.empty())
{
int w = getPixelWidth();
int h = getPixelHeight();
size_t row = 4 * w;
size_t size = row * h;
@@ -1002,26 +1121,7 @@ void Graphics::present(void *screenshotCallbackData)
throw love::Exception("Out of memory.");
}
#ifdef LOVE_IOS
SDL_SysWMinfo info = {};
SDL_VERSION(&info.version);
SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
if (info.info.uikit.resolveFramebuffer != 0)
{
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, info.info.uikit.resolveFramebuffer);
// We need to do an explicit MSAA resolve on iOS, because it uses
// GLES FBOs rather than a system framebuffer.
if (GLAD_ES_VERSION_3_0)
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
else if (GLAD_APPLE_framebuffer_multisample)
glResolveMultisampleFramebufferAPPLE();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_READ, info.info.uikit.resolveFramebuffer);
}
#endif
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getSystemBackbufferFBO());
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Replace alpha values with full opacity.
@@ -1085,6 +1185,8 @@ void Graphics::present(void *screenshotCallbackData)
if (window != nullptr)
window->swapBuffers();
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
// Reset the per-frame stat counts.
drawCalls = 0;
gl.stats.shaderSwitches = 0;
@@ -1105,6 +1207,16 @@ void Graphics::present(void *screenshotCallbackData)
}
}
int Graphics::getRequestedBackbufferMSAA() const
{
return requestedBackbufferMSAA;
}
int Graphics::getBackbufferMSAA() const
{
return internalBackbuffer.get() ? internalBackbuffer->getMSAA() : 0;
}
void Graphics::setScissor(const Rect &rect)
{
flushBatchedDraws();
@@ -1354,6 +1466,21 @@ void Graphics::setWireframe(bool enable)
states.back().wireframe = enable;
}
void *Graphics::getBufferMapMemory(size_t size)
{
// We don't need anything more complicated because get/release calls are
// never interleaved (as of when this comment was written.)
if (bufferMapMemory == nullptr || size > bufferMapMemorySize)
return malloc(size);
return bufferMapMemory;
}
void Graphics::releaseBufferMapMemory(void *mem)
{
if (mem != bufferMapMemory)
free(mem);
}
Graphics::Renderer Graphics::getRenderer() const
{
return RENDERER_OPENGL;
+20 -2
View File
@@ -63,7 +63,7 @@ public:
love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) override;
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
void unSetMode() override;
void setActive(bool active) override;
@@ -79,6 +79,9 @@ public:
void present(void *screenshotCallbackData) override;
int getRequestedBackbufferMSAA() const override;
int getBackbufferMSAA() const override;
void setColor(Colorf c) override;
void setScissor(const Rect &rect) override;
@@ -110,6 +113,9 @@ public:
// Internal use.
void cleanupRenderTexture(love::graphics::Texture *texture);
void *getBufferMapMemory(size_t size);
void releaseBufferMapMemory(void *mem);
private:
struct CachedFBOHasher
@@ -139,15 +145,27 @@ private:
void getAPIStats(int &shaderswitches) const override;
void endPass();
void bindCachedFBO(const RenderTargets &targets);
GLuint bindCachedFBO(const RenderTargets &targets);
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
void updateBackbuffer(int width, int height, int pixelwidth, int pixelheight, int msaa);
GLuint getInternalBackbufferFBO() const;
GLuint getSystemBackbufferFBO() const;
void setDebug(bool enable);
std::unordered_map<RenderTargets, GLuint, CachedFBOHasher> framebufferObjects;
bool windowHasStencil;
GLuint mainVAO;
StrongRef<love::graphics::Texture> internalBackbuffer;
StrongRef<love::graphics::Texture> internalBackbufferDepthStencil;
GLuint internalBackbufferFBO;
int requestedBackbufferMSAA;
char *bufferMapMemory;
size_t bufferMapMemorySize;
// Only needed for buffer types that can be bound to shaders.
StrongRef<love::graphics::Buffer> defaultBuffers[BUFFERTYPE_MAX_ENUM];
+1 -30
View File
@@ -752,30 +752,8 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
buffer->retain();
}
bool addbuffertoarray = true;
if (info->buffers[i] != nullptr)
{
Buffer *oldbuffer = info->buffers[i];
auto it = std::find(buffersToUnmap.begin(), buffersToUnmap.end(), oldbuffer);
if (it != buffersToUnmap.end())
{
addbuffertoarray = false;
if (buffer != nullptr)
*it = buffer;
else
{
auto last = buffersToUnmap.end() - 1;
*it = *last;
buffersToUnmap.erase(last);
}
}
oldbuffer->release();
}
if (addbuffertoarray && buffer != nullptr)
buffersToUnmap.push_back(buffer);
info->buffers[i]->release();
info->buffers[i] = buffer;
@@ -907,13 +885,6 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
GLint location = builtinUniforms[BUILTIN_UNIFORMS_PER_DRAW];
if (location >= 0)
glUniform4fv(location, 13, (const GLfloat *) &data);
// TODO: Find a better place to put this.
// Buffers used in this shader can be mapped by external code without
// unmapping. We need to make sure the data on the GPU is up to date,
// otherwise the shader can read from old data.
for (Buffer *buffer : buffersToUnmap)
buffer->unmap();
}
int Shader::getUniformTypeComponents(GLenum type) const
-2
View File
@@ -116,8 +116,6 @@ private:
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
std::vector<Buffer *> buffersToUnmap;
float lastPointSize;
}; // Shader
+1 -1
View File
@@ -499,7 +499,7 @@ love::graphics::StreamBuffer *CreateStreamBuffer(BufferType mode, size_t size)
{
// AMD's pinned memory seems to be faster than persistent mapping,
// on AMD GPUs.
if (GLAD_AMD_pinned_memory)
if (GLAD_AMD_pinned_memory && gl.getVendor() == OpenGL::VENDOR_AMD)
{
try
{
+7 -14
View File
@@ -218,6 +218,10 @@ Texture::Texture(love::graphics::Graphics *gfx, const Settings &settings, const
if (textureGLError != GL_NO_ERROR)
throw love::Exception("Cannot create Texture (OpenGL error: %s)", OpenGL::errorString(textureGLError));
}
// ImageData is referenced by the first loadVolatile call, but we don't
// hang on to it after that so we can save memory.
slices.clear();
}
Texture::~Texture()
@@ -480,14 +484,8 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
}
}
void Texture::generateMipmaps()
void Texture::generateMipmapsInternal()
{
if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE)
throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled.");
if (isPixelFormatCompressed(format))
throw love::Exception("generateMipmaps cannot be called on a compressed Texture.");
gl.bindTextureToUnit(this, 0, false);
GLenum gltextype = OpenGL::getGLTextureType(texType);
@@ -498,13 +496,10 @@ void Texture::generateMipmaps()
glGenerateMipmap(gltextype);
}
love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r)
void Texture::readbackImageData(love::image::ImageData *data, int slice, int mipmap, const Rect &r)
{
// Base class does validation (only RTs allowed, etc) and creates ImageData.
love::image::ImageData *data = love::graphics::Texture::newImageData(module, slice, mipmap, r);
if (fbo == 0) // Should never be reached.
return data;
return;
bool isSRGB = false;
OpenGL::TextureFormat fmt = gl.convertPixelFormat(data->getFormat(), false, isSRGB);
@@ -525,8 +520,6 @@ love::image::ImageData *Texture::newImageData(love::image::Image *module, int sl
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
return data;
}
void Texture::setSamplerState(const SamplerState &s)
+4 -2
View File
@@ -46,8 +46,6 @@ public:
bool loadVolatile() override;
void unloadVolatile() override;
void generateMipmaps() override;
love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
void setSamplerState(const SamplerState &s) override;
ptrdiff_t getHandle() const override;
@@ -62,6 +60,10 @@ private:
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override;
void generateMipmapsInternal() override;
void readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect) override;
Slices slices;
GLuint fbo;