mirror of
https://github.com/love2d/love.git
synced 2026-08-18 03:34:23 +02:00
Merge branch '12.0-development' into metal
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -116,8 +116,6 @@ private:
|
||||
|
||||
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
|
||||
|
||||
std::vector<Buffer *> buffersToUnmap;
|
||||
|
||||
float lastPointSize;
|
||||
|
||||
}; // Shader
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user