Merge branch '12.0-development' into metal

This commit is contained in:
Alex Szpakowski
2020-07-29 18:21:19 -03:00
57 changed files with 6870 additions and 4915 deletions
+142 -90
View File
@@ -35,18 +35,54 @@ namespace graphics
namespace opengl
{
Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
: love::graphics::Buffer(size, type, usage, mapflags)
, vbo(0)
, memory_map(nullptr)
, modified_offset(0)
, modified_size(0)
static GLenum getGLFormat(DataFormat format)
{
target = OpenGL::getGLBufferType(type);
switch (format)
{
case DATAFORMAT_FLOAT: return GL_R32F;
case DATAFORMAT_FLOAT_VEC2: return GL_RG32F;
case DATAFORMAT_FLOAT_VEC3: return GL_RGB32F;
case DATAFORMAT_FLOAT_VEC4: return GL_RGBA32F;
case DATAFORMAT_INT32: return GL_R32I;
case DATAFORMAT_INT32_VEC2: return GL_RG32I;
case DATAFORMAT_INT32_VEC3: return GL_RGB32I;
case DATAFORMAT_INT32_VEC4: return GL_RGBA32I;
case DATAFORMAT_UINT32: return GL_R32UI;
case DATAFORMAT_UINT32_VEC2: return GL_RG32UI;
case DATAFORMAT_UINT32_VEC3: return GL_RGB32UI;
case DATAFORMAT_UINT32_VEC4: return GL_RGBA32UI;
case DATAFORMAT_UNORM8_VEC4: return GL_RGBA8;
case DATAFORMAT_INT8_VEC4: return GL_RGBA8I;
case DATAFORMAT_UINT8_VEC4: return GL_RGBA8UI;
case DATAFORMAT_UNORM16_VEC2: return GL_RG16;
case DATAFORMAT_UNORM16_VEC4: return GL_RGBA16;
case DATAFORMAT_INT16_VEC2: return GL_RG16I;
case DATAFORMAT_INT16_VEC4: return GL_RGBA16I;
case DATAFORMAT_UINT16: return GL_R16UI;
case DATAFORMAT_UINT16_VEC2: return GL_RG16UI;
case DATAFORMAT_UINT16_VEC4: return GL_RGBA16UI;
default: return GL_ZERO;
}
}
Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
: love::graphics::Buffer(gfx, settings, format, size, arraylength)
{
size = getSize();
arraylength = getArrayLength();
if (typeFlags & TYPEFLAG_TEXEL)
mapType = BUFFERTYPE_TEXEL;
else if (typeFlags & TYPEFLAG_VERTEX)
mapType = BUFFERTYPE_VERTEX;
else if (typeFlags & TYPEFLAG_INDEX)
mapType = BUFFERTYPE_INDEX;
target = OpenGL::getGLBufferType(mapType);
try
{
memory_map = new char[size];
memoryMap = new char[size];
}
catch (std::bad_alloc &)
{
@@ -54,34 +90,78 @@ Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usa
}
if (data != nullptr)
memcpy(memory_map, data, size);
memcpy(memoryMap, data, size);
if (!load(data != nullptr))
{
delete[] memory_map;
throw love::Exception("Could not load vertex buffer (out of VRAM?)");
unloadVolatile();
delete[] memoryMap;
throw love::Exception("Could not create buffer (out of VRAM?)");
}
}
Buffer::~Buffer()
{
if (vbo != 0)
unload();
unloadVolatile();
delete[] memoryMap;
}
delete[] memory_map;
bool Buffer::loadVolatile()
{
if (buffer != 0)
return true;
return load(true);
}
void Buffer::unloadVolatile()
{
mapped = false;
if (buffer != 0)
gl.deleteBuffer(buffer);
buffer = 0;
if (texture != 0)
gl.deleteTexture(texture);
texture = 0;
}
bool Buffer::load(bool restore)
{
while (glGetError() != GL_NO_ERROR)
/* Clear the error buffer. */;
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()));
if (getTypeFlags() & TYPEFLAG_TEXEL)
{
glGenTextures(1, &texture);
gl.bindBufferTextureToUnit(texture, 0, false, true);
glTexBuffer(target, getGLFormat(getDataMember(0).decl.format), buffer);
}
return (glGetError() == GL_NO_ERROR);
}
void *Buffer::map()
{
if (is_mapped)
return memory_map;
if (mapped)
return memoryMap;
is_mapped = true;
mapped = true;
modified_offset = 0;
modified_size = 0;
modifiedOffset = 0;
modifiedSize = 0;
isMappedDataModified = false;
return memory_map;
return memoryMap;
}
void Buffer::unmapStatic(size_t offset, size_t size)
@@ -90,8 +170,8 @@ void Buffer::unmapStatic(size_t offset, size_t size)
return;
// Upload the mapped data to the buffer.
gl.bindBuffer(type, vbo);
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
gl.bindBuffer(mapType, buffer);
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memoryMap + offset);
}
void Buffer::unmapStream()
@@ -100,133 +180,105 @@ void Buffer::unmapStream()
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
gl.bindBuffer(type, vbo);
gl.bindBuffer(mapType, buffer);
glBufferData(target, (GLsizeiptr) getSize(), nullptr, glusage);
#if LOVE_WINDOWS
// TODO: Verify that this codepath is a useful optimization.
if (gl.getVendor() == OpenGL::VENDOR_INTEL)
glBufferData(target, (GLsizeiptr) getSize(), memory_map, glusage);
glBufferData(target, (GLsizeiptr) getSize(), memoryMap, glusage);
else
#endif
glBufferSubData(target, 0, (GLsizeiptr) getSize(), memory_map);
glBufferSubData(target, 0, (GLsizeiptr) getSize(), memoryMap);
}
void Buffer::unmap()
{
if (!is_mapped)
if (!mapped)
return;
if ((map_flags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
mapped = false;
if ((mapFlags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
{
modified_offset = std::min(modified_offset, getSize() - 1);
modified_size = std::min(modified_size, getSize() - modified_offset);
if (!isMappedDataModified)
return;
modifiedOffset = std::min(modifiedOffset, getSize() - 1);
modifiedSize = std::min(modifiedSize, getSize() - modifiedOffset);
}
else
{
modified_offset = 0;
modified_size = getSize();
modifiedOffset = 0;
modifiedSize = getSize();
}
if (modified_size > 0)
if (modifiedSize > 0)
{
switch (getUsage())
{
case vertex::USAGE_STATIC:
unmapStatic(modified_offset, modified_size);
case BUFFERUSAGE_STATIC:
unmapStatic(modifiedOffset, modifiedSize);
break;
case vertex::USAGE_STREAM:
case BUFFERUSAGE_STREAM:
unmapStream();
break;
case vertex::USAGE_DYNAMIC:
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 (modified_size >= getSize() / 3)
if (modifiedSize >= getSize() / 3)
unmapStream();
else
unmapStatic(modified_offset, modified_size);
unmapStatic(modifiedOffset, modifiedSize);
break;
}
}
modified_offset = 0;
modified_size = 0;
is_mapped = false;
modifiedOffset = 0;
modifiedSize = 0;
}
void Buffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
{
if (!is_mapped || !(map_flags & MAP_EXPLICIT_RANGE_MODIFY))
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 old_range_end = modified_offset + modified_size;
modified_offset = std::min(modified_offset, offset);
size_t oldrangeend = modifiedOffset + modifiedSize;
modifiedOffset = std::min(modifiedOffset, offset);
size_t new_range_end = std::max(offset + modifiedsize, old_range_end);
modified_size = new_range_end - modified_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(memory_map + offset, data, size);
memcpy(memoryMap + offset, data, size);
if (is_mapped)
if (mapped)
setMappedRangeModified(offset, size);
else
{
gl.bindBuffer(type, vbo);
gl.bindBuffer(mapType, buffer);
glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data);
}
}
ptrdiff_t Buffer::getHandle() const
{
return vbo;
}
void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
{
other->fill(otheroffset, size, memory_map + offset);
}
bool Buffer::loadVolatile()
{
return load(true);
}
void Buffer::unloadVolatile()
{
unload();
}
bool Buffer::load(bool restore)
{
glGenBuffers(1, &vbo);
gl.bindBuffer(type, vbo);
while (glGetError() != GL_NO_ERROR)
/* Clear the error buffer. */;
// Copy the old buffer only if 'restore' was requested.
const GLvoid *src = restore ? memory_map : nullptr;
// Note that if 'src' is '0', no data will be copied.
glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
return (glGetError() == GL_NO_ERROR);
}
void Buffer::unload()
{
is_mapped = false;
gl.deleteBuffer(vbo);
vbo = 0;
other->fill(otheroffset, size, memoryMap + offset);
}
} // opengl
+25 -16
View File
@@ -32,6 +32,9 @@ namespace love
{
namespace graphics
{
class Graphics;
namespace opengl
{
@@ -39,39 +42,45 @@ class Buffer final : public love::graphics::Buffer, public Volatile
{
public:
Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags);
Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength);
virtual ~Buffer();
void *map() override;
void unmap() override;
void setMappedRangeModified(size_t offset, size_t size) override;
void fill(size_t offset, size_t size, const void *data) override;
ptrdiff_t getHandle() const override;
void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
// Implements Volatile.
bool loadVolatile() override;
void unloadVolatile() override;
void *map() override;
void unmap() override;
void setMappedRangeModified(size_t offset, size_t size) 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);
void unload();
void unmapStatic(size_t offset, size_t size);
void unmapStream();
GLenum target;
BufferType mapType = BUFFERTYPE_VERTEX;
GLenum target = 0;
// The VBO identifier. Assigned by OpenGL.
GLuint vbo;
// The buffer object identifier. Assigned by OpenGL.
GLuint buffer = 0;
// Used for Texel Buffer types.
GLuint texture = 0;
// A pointer to mapped memory.
char *memory_map;
char *memoryMap = nullptr;
size_t modified_offset;
size_t modified_size;
size_t modifiedOffset = 0;
size_t modifiedSize = 0;
bool isMappedDataModified = false;
}; // Buffer
+49 -36
View File
@@ -107,6 +107,7 @@ love::graphics::Graphics *createInstance()
Graphics::Graphics()
: windowHasStencil(false)
, mainVAO(0)
, defaultBuffers()
, supportedFormats()
{
gl = OpenGL();
@@ -162,9 +163,9 @@ love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage
return new Shader(vertex, pixel);
}
love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
{
return new Buffer(size, data, type, usage, mapflags);
return new Buffer(this, settings, format, data, size, arraylength);
}
void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
@@ -255,11 +256,32 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
{
// Initial sizes that should be good enough for most cases. It will
// resize to fit if needed, later.
batchedDrawState.vb[0] = CreateStreamBuffer(BUFFER_VERTEX, 1024 * 1024 * 1);
batchedDrawState.vb[1] = CreateStreamBuffer(BUFFER_VERTEX, 256 * 1024 * 1);
batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFER_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
batchedDrawState.vb[0] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 1024 * 1024 * 1);
batchedDrawState.vb[1] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 256 * 1024 * 1);
batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFERTYPE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
}
if (capabilities.features[FEATURE_TEXEL_BUFFER] && defaultBuffers[BUFFERTYPE_TEXEL].get() == nullptr)
{
Buffer::Settings settings(Buffer::TYPEFLAG_TEXEL, 0, BUFFERUSAGE_STATIC);
std::vector<Buffer::DataDeclaration> format = {{"", DATAFORMAT_FLOAT_VEC4, 0}};
const float texel[] = {0.0f, 0.0f, 0.0f, 1.0f};
love::graphics::Buffer *buffer = newBuffer(settings, format, texel, sizeof(texel), 1);
defaultBuffers[BUFFERTYPE_TEXEL].set(buffer, Acquire::NORETAIN);
}
// Load default resources before other Volatile.
for (int i = 0; i < BUFFERTYPE_MAX_ENUM; i++)
{
if (defaultBuffers[i].get())
((Buffer *) defaultBuffers[i].get())->loadVolatile();
}
if (defaultBuffers[BUFFERTYPE_TEXEL].get())
gl.setDefaultTexelBuffer((GLuint) defaultBuffers[BUFFERTYPE_TEXEL]->getTexelBufferHandle());
// Reload all volatile objects.
if (!Volatile::loadAll())
::printf("Could not reload all volatile objects.\n");
@@ -269,12 +291,11 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
// Restore the graphics state.
restoreState(states.back());
int gammacorrect = isGammaCorrect() ? 1 : 0;
Shader::Language target = getShaderLanguageTarget();
// We always need a default shader.
for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++)
{
auto stype = (Shader::StandardShader) i;
if (i == Shader::STANDARD_ARRAY && !capabilities.textureTypes[TEXTURE_2D_ARRAY])
continue;
@@ -284,8 +305,10 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
{
if (!Shader::standardShaders[i])
{
const auto &code = defaultShaderCode[i][target][gammacorrect];
Shader::standardShaders[i] = love::graphics::Graphics::newShader(code.source[ShaderStage::STAGE_VERTEX], code.source[ShaderStage::STAGE_PIXEL]);
std::vector<std::string> stages;
stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_VERTEX));
stages.push_back(Shader::getDefaultCode(stype, ShaderStage::STAGE_PIXEL));
Shader::standardShaders[i] = newShader(stages);
}
}
catch (love::Exception &)
@@ -376,7 +399,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType);
GLenum gldatatype = OpenGL::getGLIndexDataType(cmd.indexType);
gl.bindBuffer(BUFFER_INDEX, cmd.indexBuffer->getHandle());
gl.bindBuffer(BUFFERTYPE_INDEX, cmd.indexBuffer->getHandle());
if (cmd.instanceCount > 1)
glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount);
@@ -386,13 +409,13 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
++drawCalls;
}
static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::BufferBindings &buffers, int vertexcount)
static inline void advanceVertexOffsets(const VertexAttributes &attributes, BufferBindings &buffers, int vertexcount)
{
// TODO: Figure out a better way to avoid touching the same buffer multiple
// times, if multiple attributes share the buffer.
uint32 touchedbuffers = 0;
for (unsigned int i = 0; i < vertex::Attributes::MAX; i++)
for (unsigned int i = 0; i < VertexAttributes::MAX; i++)
{
if (!attributes.isEnabled(i))
continue;
@@ -409,7 +432,7 @@ static inline void advanceVertexOffsets(const vertex::Attributes &attributes, ve
}
}
void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture)
void Graphics::drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture)
{
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
@@ -418,7 +441,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu
gl.bindTextureToUnit(texture, 0, false);
gl.setCullMode(CULL_NONE);
gl.bindBuffer(BUFFER_INDEX, quadIndexBuffer->getHandle());
gl.bindBuffer(BUFFERTYPE_INDEX, quadIndexBuffer->getHandle());
if (gl.isBaseVertexSupported())
{
@@ -438,7 +461,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu
}
else
{
vertex::BufferBindings bufferscopy = buffers;
BufferBindings bufferscopy = buffers;
if (start > 0)
advanceVertexOffsets(attributes, bufferscopy, start * 4);
@@ -525,7 +548,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
endPass();
bool iswindow = rts.getFirstTarget().texture == nullptr;
vertex::Winding vertexwinding = state.winding;
Winding vertexwinding = state.winding;
if (iswindow)
{
@@ -543,10 +566,10 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
// Flip front face winding when rendering to a texture, since our
// projection matrix is flipped.
vertexwinding = vertexwinding == vertex::WINDING_CW ? vertex::WINDING_CCW : vertex::WINDING_CW;
vertexwinding = vertexwinding == WINDING_CW ? WINDING_CCW : WINDING_CW;
}
glFrontFace(vertexwinding == vertex::WINDING_CW ? GL_CW : GL_CCW);
glFrontFace(vertexwinding == WINDING_CW ? GL_CW : GL_CCW);
gl.setViewport({0, 0, pixelw, pixelh});
@@ -1246,7 +1269,7 @@ void Graphics::setDepthMode(CompareMode compare, bool write)
}
}
void Graphics::setFrontFaceWinding(vertex::Winding winding)
void Graphics::setFrontFaceWinding(Winding winding)
{
DisplayState &state = states.back();
@@ -1256,9 +1279,9 @@ void Graphics::setFrontFaceWinding(vertex::Winding winding)
state.winding = winding;
if (isRenderTargetActive())
winding = winding == vertex::WINDING_CW ? vertex::WINDING_CCW : vertex::WINDING_CW;
winding = winding == WINDING_CW ? WINDING_CCW : WINDING_CW;
glFrontFace(winding == vertex::WINDING_CW ? GL_CW : GL_CCW);
glFrontFace(winding == WINDING_CW ? GL_CW : GL_CCW);
}
void Graphics::setColor(Colorf c)
@@ -1388,17 +1411,19 @@ void Graphics::initCapabilities()
capabilities.features[FEATURE_GLSL3] = GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
capabilities.features[FEATURE_GLSL4] = GLAD_ES_VERSION_3_1 || (gl.isCoreProfile() && GLAD_VERSION_4_3);
capabilities.features[FEATURE_INSTANCING] = gl.isInstancingSupported();
static_assert(FEATURE_MAX_ENUM == 10, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.features[FEATURE_TEXEL_BUFFER] = gl.areTexelBuffersSupported();
static_assert(FEATURE_MAX_ENUM == 11, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize();
capabilities.limits[LIMIT_TEXTURE_LAYERS] = gl.getMaxTextureLayers();
capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = gl.getMax3DTextureSize();
capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = gl.getMaxCubeTextureSize();
capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = gl.getMaxTexelBufferSize();
capabilities.limits[LIMIT_RENDER_TARGETS] = gl.getMaxRenderTargets();
capabilities.limits[LIMIT_TEXTURE_MSAA] = gl.getMaxSamples();
capabilities.limits[LIMIT_ANISOTROPY] = gl.getMaxAnisotropy();
static_assert(LIMIT_MAX_ENUM == 8, "Graphics::initCapabilities must be updated when adding a new system limit!");
static_assert(LIMIT_MAX_ENUM == 9, "Graphics::initCapabilities must be updated when adding a new system limit!");
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
@@ -1524,18 +1549,6 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
return supported.value;
}
Shader::Language Graphics::getShaderLanguageTarget() const
{
if (gl.isCoreProfile())
return Shader::LANGUAGE_GLSL3;
else if (GLAD_ES_VERSION_3_0)
return Shader::LANGUAGE_ESSL3;
else if (GLAD_ES_VERSION_2_0)
return Shader::LANGUAGE_ESSL1;
else
return Shader::LANGUAGE_GLSL1;
}
} // opengl
} // graphics
} // love
+6 -5
View File
@@ -60,7 +60,7 @@ public:
const char *getName() const override;
love::graphics::Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) override;
love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
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 windowhasstencil) override;
@@ -70,7 +70,7 @@ public:
void draw(const DrawCommand &cmd) override;
void draw(const DrawIndexedCommand &cmd) override;
void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture) override;
void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture) override;
void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override;
void clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth) override;
@@ -91,7 +91,7 @@ public:
void setDepthMode(CompareMode compare, bool write) override;
void setFrontFaceWinding(vertex::Winding winding) override;
void setFrontFaceWinding(Winding winding) override;
void setColorMask(ColorChannelMask mask) override;
@@ -107,8 +107,6 @@ public:
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
Shader::Language getShaderLanguageTarget() const override;
// Internal use.
void cleanupRenderTexture(love::graphics::Texture *texture);
@@ -150,6 +148,9 @@ private:
bool windowHasStencil;
GLuint mainVAO;
// Only needed for buffer types that can be bound to shaders.
StrongRef<love::graphics::Buffer> defaultBuffers[BUFFERTYPE_MAX_ENUM];
// [rendertarget][readable][srgb]
OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2];
+201 -106
View File
@@ -155,11 +155,11 @@ bool OpenGL::initContext()
#ifdef LOVE_WINDOWS
if (getVendor() == VENDOR_AMD)
{
// Radeon HD drivers switched from "ATI Radeon" to "AMD Radeon" around
// Radeon drivers switched from "ATI Radeon" to "AMD Radeon" around
// the 7000 series. We'll assume this bug doesn't affect those newer
// GPUs / drivers.
const char *device = (const char *) glGetString(GL_RENDERER);
if (strstr(device, "ATI Radeon HD ") || strstr(device, "ATI Mobility Radeon HD"))
if (strstr(device, "ATI Radeon") || strstr(device, "ATI Mobility Radeon"))
bugs.texStorageBreaksSubImage = true;
}
#endif
@@ -185,7 +185,7 @@ void OpenGL::setupContext()
state.enabledAttribArrays = (uint32) ((1ull << uint32(maxvertexattribs)) - 1);
state.instancedAttribArrays = 0;
setVertexAttributes(vertex::Attributes(), vertex::BufferBindings());
setVertexAttributes(VertexAttributes(), BufferBindings());
// Get the current viewport.
glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
@@ -222,14 +222,14 @@ void OpenGL::setupContext()
glGetIntegerv(GL_CULL_FACE_MODE, &faceCull);
state.faceCullMode = faceCull;
for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
{
state.boundBuffers[i] = 0;
glBindBuffer(getGLBufferType((BufferType) i), 0);
}
// Initialize multiple texture unit support for shaders.
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
for (int i = 0; i < TEXTURE_MAX_ENUM + 1; i++)
{
state.boundTextures[i].clear();
state.boundTextures[i].resize(maxTextureUnits, 0);
@@ -280,6 +280,10 @@ void OpenGL::deInitContext()
}
}
if (state.defaultTexelBuffer != 0)
gl.deleteTexture(state.defaultTexelBuffer);
state.defaultTexelBuffer = 0;
contextInitialized = false;
}
@@ -459,6 +463,11 @@ void OpenGL::initMaxValues()
else
maxTextureArrayLayers = 0;
if (areTexelBuffersSupported())
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTexelBufferSize);
else
maxTexelBufferSize = 0;
int maxattachments = 1;
int maxdrawbuffers = 1;
@@ -562,16 +571,11 @@ GLenum OpenGL::getGLPrimitiveType(PrimitiveType type)
{
switch (type)
{
case PRIMITIVE_TRIANGLES:
return GL_TRIANGLES;
case PRIMITIVE_TRIANGLE_STRIP:
return GL_TRIANGLE_STRIP;
case PRIMITIVE_TRIANGLE_FAN:
return GL_TRIANGLE_FAN;
case PRIMITIVE_POINTS:
return GL_POINTS;
case PRIMITIVE_MAX_ENUM:
return GL_ZERO;
case PRIMITIVE_TRIANGLES: return GL_TRIANGLES;
case PRIMITIVE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
case PRIMITIVE_TRIANGLE_FAN: return GL_TRIANGLE_FAN;
case PRIMITIVE_POINTS: return GL_POINTS;
case PRIMITIVE_MAX_ENUM: return GL_ZERO;
}
return GL_ZERO;
@@ -581,12 +585,10 @@ GLenum OpenGL::getGLBufferType(BufferType type)
{
switch (type)
{
case BUFFER_VERTEX:
return GL_ARRAY_BUFFER;
case BUFFER_INDEX:
return GL_ELEMENT_ARRAY_BUFFER;
case BUFFER_MAX_ENUM:
return GL_ZERO;
case BUFFERTYPE_VERTEX: return GL_ARRAY_BUFFER;
case BUFFERTYPE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
case BUFFERTYPE_TEXEL: return GL_TEXTURE_BUFFER;
case BUFFERTYPE_MAX_ENUM: return GL_ZERO;
}
return GL_ZERO;
@@ -596,16 +598,11 @@ GLenum OpenGL::getGLTextureType(TextureType type)
{
switch (type)
{
case TEXTURE_2D:
return GL_TEXTURE_2D;
case TEXTURE_VOLUME:
return GL_TEXTURE_3D;
case TEXTURE_2D_ARRAY:
return GL_TEXTURE_2D_ARRAY;
case TEXTURE_CUBE:
return GL_TEXTURE_CUBE_MAP;
case TEXTURE_MAX_ENUM:
return GL_ZERO;
case TEXTURE_2D: return GL_TEXTURE_2D;
case TEXTURE_VOLUME: return GL_TEXTURE_3D;
case TEXTURE_2D_ARRAY: return GL_TEXTURE_2D_ARRAY;
case TEXTURE_CUBE: return GL_TEXTURE_CUBE_MAP;
case TEXTURE_MAX_ENUM: return GL_TEXTURE_BUFFER; // Hack
}
return GL_ZERO;
@@ -615,74 +612,159 @@ GLenum OpenGL::getGLIndexDataType(IndexDataType type)
{
switch (type)
{
case INDEX_UINT16:
return GL_UNSIGNED_SHORT;
case INDEX_UINT32:
return GL_UNSIGNED_INT;
default:
return GL_ZERO;
case INDEX_UINT16: return GL_UNSIGNED_SHORT;
case INDEX_UINT32: return GL_UNSIGNED_INT;
default: return GL_ZERO;
}
}
GLenum OpenGL::getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat)
GLenum OpenGL::getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat)
{
normalized = GL_FALSE;
intformat = false;
components = 1;
switch (type)
switch (format)
{
case vertex::DATA_SNORM8:
normalized = GL_TRUE;
return GL_BYTE;
case vertex::DATA_UNORM8:
normalized = GL_TRUE;
return GL_UNSIGNED_BYTE;
case vertex::DATA_INT8:
intformat = true;
return GL_BYTE;
case vertex::DATA_UINT8:
intformat = true;
return GL_UNSIGNED_BYTE;
case vertex::DATA_SNORM16:
normalized = GL_TRUE;
return GL_SHORT;
case vertex::DATA_UNORM16:
normalized = GL_TRUE;
return GL_UNSIGNED_SHORT;
case vertex::DATA_INT16:
intformat = true;
return GL_SHORT;
case vertex::DATA_UINT16:
intformat = true;
return GL_UNSIGNED_SHORT;
case vertex::DATA_INT32:
case DATAFORMAT_FLOAT:
components = 1;
return GL_FLOAT;
case DATAFORMAT_FLOAT_VEC2:
components = 2;
return GL_FLOAT;
case DATAFORMAT_FLOAT_VEC3:
components = 3;
return GL_FLOAT;
case DATAFORMAT_FLOAT_VEC4:
components = 4;
return GL_FLOAT;
case DATAFORMAT_FLOAT_MAT2X2:
case DATAFORMAT_FLOAT_MAT2X3:
case DATAFORMAT_FLOAT_MAT2X4:
case DATAFORMAT_FLOAT_MAT3X2:
case DATAFORMAT_FLOAT_MAT3X3:
case DATAFORMAT_FLOAT_MAT3X4:
case DATAFORMAT_FLOAT_MAT4X2:
case DATAFORMAT_FLOAT_MAT4X3:
case DATAFORMAT_FLOAT_MAT4X4:
return GL_ZERO;
case DATAFORMAT_INT32:
components = 1;
intformat = true;
return GL_INT;
case vertex::DATA_UINT32:
case DATAFORMAT_INT32_VEC2:
components = 2;
intformat = true;
return GL_INT;
case DATAFORMAT_INT32_VEC3:
components = 3;
intformat = true;
return GL_INT;
case DATAFORMAT_INT32_VEC4:
components = 4;
intformat = true;
return GL_INT;
case DATAFORMAT_UINT32:
components = 1;
intformat = true;
return GL_UNSIGNED_INT;
case vertex::DATA_FLOAT:
normalized = GL_FALSE;
return GL_FLOAT;
case vertex::DATA_MAX_ENUM:
case DATAFORMAT_UINT32_VEC2:
components = 2;
intformat = true;
return GL_UNSIGNED_INT;
case DATAFORMAT_UINT32_VEC3:
components = 3;
intformat = true;
return GL_UNSIGNED_INT;
case DATAFORMAT_UINT32_VEC4:
components = 4;
intformat = true;
return GL_UNSIGNED_INT;
case DATAFORMAT_SNORM8_VEC4:
components = 4;
normalized = GL_TRUE;
return GL_BYTE;
case DATAFORMAT_UNORM8_VEC4:
components = 4;
normalized = GL_TRUE;
return GL_UNSIGNED_BYTE;
case DATAFORMAT_INT8_VEC4:
components = 4;
intformat = true;
return GL_BYTE;
case DATAFORMAT_UINT8_VEC4:
components = 4;
intformat = true;
return GL_UNSIGNED_BYTE;
case DATAFORMAT_SNORM16_VEC2:
components = 2;
normalized = GL_TRUE;
return GL_BYTE;
case DATAFORMAT_SNORM16_VEC4:
components = 4;
normalized = GL_TRUE;
return GL_BYTE;
case DATAFORMAT_UNORM16_VEC2:
components = 2;
normalized = GL_TRUE;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_UNORM16_VEC4:
components = 4;
normalized = GL_TRUE;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_INT16_VEC2:
components = 2;
intformat = true;
return GL_SHORT;
case DATAFORMAT_INT16_VEC4:
components = 4;
intformat = true;
return GL_SHORT;
case DATAFORMAT_UINT16:
components = 1;
intformat = true;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_UINT16_VEC2:
components = 2;
intformat = true;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_UINT16_VEC4:
components = 4;
intformat = true;
return GL_UNSIGNED_SHORT;
case DATAFORMAT_BOOL:
case DATAFORMAT_BOOL_VEC2:
case DATAFORMAT_BOOL_VEC3:
case DATAFORMAT_BOOL_VEC4:
return GL_ZERO;
case DATAFORMAT_MAX_ENUM:
return GL_ZERO;
}
return GL_ZERO;
}
GLenum OpenGL::getGLBufferUsage(vertex::Usage usage)
GLenum OpenGL::getGLBufferUsage(BufferUsage usage)
{
switch (usage)
{
case vertex::USAGE_STREAM:
return GL_STREAM_DRAW;
case vertex::USAGE_DYNAMIC:
return GL_DYNAMIC_DRAW;
case vertex::USAGE_STATIC:
return GL_STATIC_DRAW;
default:
return 0;
case BUFFERUSAGE_STREAM: return GL_STREAM_DRAW;
case BUFFERUSAGE_DYNAMIC: return GL_DYNAMIC_DRAW;
case BUFFERUSAGE_STATIC: return GL_STATIC_DRAW;
default: return 0;
}
}
@@ -699,14 +781,14 @@ void OpenGL::deleteBuffer(GLuint buffer)
{
glDeleteBuffers(1, &buffer);
for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
{
if (state.boundBuffers[i] == buffer)
state.boundBuffers[i] = 0;
}
}
void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const vertex::BufferBindings &buffers)
void OpenGL::setVertexAttributes(const VertexAttributes &attributes, const BufferBindings &buffers)
{
uint32 enablediff = attributes.enableBits ^ state.enabledAttribArrays;
uint32 instanceattribbits = 0;
@@ -739,18 +821,19 @@ void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const ver
if ((state.instancedAttribArrays & bit) ^ divisorbit)
glVertexAttribDivisor(i, divisor);
int components = 0;
GLboolean normalized = GL_FALSE;
bool intformat = false;
GLenum gltype = getGLVertexDataType(attrib.type, normalized, intformat);
GLenum gltype = getGLVertexDataType(attrib.format, components, normalized, intformat);
const void *offsetpointer = reinterpret_cast<void*>(bufferinfo.offset + attrib.offsetFromVertex);
bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
bindBuffer(BUFFERTYPE_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
if (intformat)
glVertexAttribIPointer(i, attrib.components, gltype, layout.stride, offsetpointer);
glVertexAttribIPointer(i, components, gltype, layout.stride, offsetpointer);
else
glVertexAttribPointer(i, attrib.components, gltype, normalized, layout.stride, offsetpointer);
glVertexAttribPointer(i, components, gltype, normalized, layout.stride, offsetpointer);
}
i++;
@@ -988,7 +1071,7 @@ void OpenGL::setTextureUnit(int textureunit)
state.curTextureUnit = textureunit;
}
void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev)
void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit)
{
if (texture != state.boundTextures[target][textureunit])
{
@@ -1004,9 +1087,19 @@ void OpenGL::bindTextureToUnit(TextureType target, GLuint texture, int textureun
else
state.curTextureUnit = textureunit;
}
else if (bindforedit && !restoreprev && textureunit != state.curTextureUnit)
{
glActiveTexture(GL_TEXTURE0 + textureunit);
state.curTextureUnit = textureunit;
}
}
void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev)
void OpenGL::bindBufferTextureToUnit(GLuint texture, int textureunit, bool restoreprev, bool bindforedit)
{
bindTextureToUnit(TEXTURE_MAX_ENUM, texture, textureunit, restoreprev, bindforedit);
}
void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit)
{
TextureType textype = TEXTURE_2D;
GLuint handle = 0;
@@ -1028,14 +1121,14 @@ void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restorepr
handle = getDefaultTexture(textype);
}
bindTextureToUnit(textype, handle, textureunit, restoreprev);
bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
}
void OpenGL::deleteTexture(GLuint texture)
{
// glDeleteTextures binds texture 0 to all texture units the deleted texture
// was bound to before deletion.
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
for (int i = 0; i < TEXTURE_MAX_ENUM + 1; i++)
{
for (GLuint &texid : state.boundTextures[i])
{
@@ -1068,24 +1161,15 @@ GLint OpenGL::getGLCompareMode(CompareMode mode)
{
switch (mode)
{
case COMPARE_LESS:
return GL_LESS;
case COMPARE_LEQUAL:
return GL_LEQUAL;
case COMPARE_EQUAL:
return GL_EQUAL;
case COMPARE_GEQUAL:
return GL_GEQUAL;
case COMPARE_GREATER:
return GL_GREATER;
case COMPARE_NOTEQUAL:
return GL_NOTEQUAL;
case COMPARE_ALWAYS:
return GL_ALWAYS;
case COMPARE_NEVER:
return GL_NEVER;
default:
return GL_NEVER;
case COMPARE_LESS: return GL_LESS;
case COMPARE_LEQUAL: return GL_LEQUAL;
case COMPARE_EQUAL: return GL_EQUAL;
case COMPARE_GEQUAL: return GL_GEQUAL;
case COMPARE_GREATER: return GL_GREATER;
case COMPARE_NOTEQUAL: return GL_NOTEQUAL;
case COMPARE_ALWAYS: return GL_ALWAYS;
case COMPARE_NEVER: return GL_NEVER;
default: return GL_NEVER;
}
}
@@ -1333,6 +1417,12 @@ bool OpenGL::isMultiFormatMRTSupported() const
return getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
}
bool OpenGL::areTexelBuffersSupported() const
{
// Not supported in ES until 3.2, which we don't support shaders for...
return GLAD_VERSION_3_1;
}
int OpenGL::getMax2DTextureSize() const
{
return std::max(max2DTextureSize, 1);
@@ -1353,6 +1443,11 @@ int OpenGL::getMaxTextureLayers() const
return std::max(maxTextureArrayLayers, 1);
}
int OpenGL::getMaxTexelBufferSize() const
{
return maxTexelBufferSize;
}
int OpenGL::getMaxRenderTargets() const
{
return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS);
+25 -8
View File
@@ -166,7 +166,7 @@ public:
* initial full-size one (determined after some investigation with an
* affected user on Discord.)
* https://bitbucket.org/rude/love/issues/1436/bug-with-lovegraphicsprint-on-older-ati
*
* https://github.com/love2d/love/issues/1563
**/
bool texStorageBreaksSubImage;
@@ -238,7 +238,7 @@ public:
/**
* Set all vertex attribute state.
**/
void setVertexAttributes(const vertex::Attributes &attributes, const vertex::BufferBindings &buffers);
void setVertexAttributes(const VertexAttributes &attributes, const BufferBindings &buffers);
/**
* Wrapper for glCullFace which eliminates redundant state setting.
@@ -306,6 +306,12 @@ public:
**/
GLuint getDefaultTexture(TextureType type) const;
/**
* Gets the texture ID for love's default texel buffer.
**/
GLuint getDefaultTexelBuffer() const { return state.defaultTexelBuffer; }
void setDefaultTexelBuffer(GLuint tex) { state.defaultTexelBuffer = tex; }
/**
* Helper for setting the active texture unit.
*
@@ -318,9 +324,12 @@ public:
*
* @param textureunit Index in the range of [0, maxtextureunits-1]
* @param restoreprev Restore previously bound texture unit when done.
* @param bindforedit If false, the active texture unit may be left alone.
**/
void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev);
void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev);
void bindTextureToUnit(TextureType target, GLuint texture, int textureunit, bool restoreprev, bool bindforedit = true);
void bindTextureToUnit(Texture *texture, int textureunit, bool restoreprev, bool bindforedit = true);
void bindBufferTextureToUnit(GLuint texture, int textureunit, bool restoreprev, bool bindforedit);
/**
* Helper for deleting an OpenGL texture.
@@ -348,6 +357,7 @@ public:
bool isSamplerLODBiasSupported() const;
bool isBaseVertexSupported() const;
bool isMultiFormatMRTSupported() const;
bool areTexelBuffersSupported() const;
/**
* Returns the maximum supported width or height of a texture.
@@ -357,6 +367,11 @@ public:
int getMaxCubeTextureSize() const;
int getMaxTextureLayers() const;
/**
* Returns the maximum number of values in a texel buffer.
**/
int getMaxTexelBufferSize() const;
/**
* Returns the maximum supported number of simultaneous render targets.
**/
@@ -398,8 +413,8 @@ public:
static GLenum getGLPrimitiveType(PrimitiveType type);
static GLenum getGLBufferType(BufferType type);
static GLenum getGLIndexDataType(IndexDataType type);
static GLenum getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat);
static GLenum getGLBufferUsage(vertex::Usage usage);
static GLenum getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat);
static GLenum getGLBufferUsage(BufferUsage usage);
static GLenum getGLTextureType(TextureType type);
static GLint getGLWrapMode(SamplerState::WrapMode wmode);
static GLint getGLCompareMode(CompareMode mode);
@@ -435,6 +450,7 @@ private:
int max3DTextureSize;
int maxCubeTextureSize;
int maxTextureArrayLayers;
int maxTexelBufferSize;
int maxRenderTargets;
int maxSamples;
int maxTextureUnits;
@@ -447,10 +463,10 @@ private:
// Tracked OpenGL state.
struct
{
GLuint boundBuffers[BUFFER_MAX_ENUM];
GLuint boundBuffers[BUFFERTYPE_MAX_ENUM];
// Texture unit state (currently bound texture for each texture unit.)
std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM];
std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM + 1];
bool enableState[ENABLE_MAX_ENUM];
@@ -471,6 +487,7 @@ private:
GLuint boundFramebuffers[2];
GLuint defaultTexture[TEXTURE_MAX_ENUM];
GLuint defaultTexelBuffer;
} state;
+208 -19
View File
@@ -24,6 +24,7 @@
#include "Shader.h"
#include "ShaderStage.h"
#include "Graphics.h"
#include "graphics/vertex.h"
// C++
#include <algorithm>
@@ -68,6 +69,16 @@ Shader::~Shader()
delete[] p.second.textures;
}
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
{
for (int i = 0; i < p.second.count; i++)
{
if (p.second.buffers[i] != nullptr)
p.second.buffers[i]->release();
}
delete[] p.second.buffers;
}
}
}
@@ -106,6 +117,7 @@ void Shader::mapActiveUniforms()
u.location = glGetUniformLocation(program, u.name.c_str());
u.baseType = getUniformBaseType(gltype);
u.textureType = getUniformTextureType(gltype);
u.texelBufferType = getUniformTexelBufferType(gltype);
u.isDepthSampler = isDepthTextureType(gltype);
if (u.baseType == UNIFORM_MATRIX)
@@ -129,12 +141,22 @@ void Shader::mapActiveUniforms()
if (u.location == -1)
continue;
if (u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN)
if ((u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN) || u.baseType == UNIFORM_TEXELBUFFER)
{
TextureUnit unit;
unit.type = u.textureType;
unit.active = true;
unit.texture = gl.getDefaultTexture(u.textureType);
if (u.baseType == UNIFORM_TEXELBUFFER)
{
unit.isTexelBuffer = true;
unit.texture = gl.getDefaultTexelBuffer();
}
else
{
unit.isTexelBuffer = false;
unit.texture = gl.getDefaultTexture(u.textureType);
}
for (int i = 0; i < u.count; i++)
textureUnits.push_back(unit);
@@ -164,6 +186,7 @@ void Shader::mapActiveUniforms()
case UNIFORM_INT:
case UNIFORM_BOOL:
case UNIFORM_SAMPLER:
case UNIFORM_TEXELBUFFER:
u.dataSize = sizeof(int) * u.components * u.count;
u.data = malloc(u.dataSize);
break;
@@ -183,7 +206,7 @@ void Shader::mapActiveUniforms()
{
memset(u.data, 0, u.dataSize);
if (u.baseType == UNIFORM_SAMPLER)
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_TEXELBUFFER)
{
int startunit = (int) textureUnits.size() - u.count;
@@ -195,8 +218,16 @@ void Shader::mapActiveUniforms()
glUniform1iv(u.location, u.count, u.ints);
u.textures = new love::graphics::Texture*[u.count];
memset(u.textures, 0, sizeof(Texture *) * u.count);
if (u.baseType == UNIFORM_TEXELBUFFER)
{
u.buffers = new love::graphics::Buffer*[u.count];
memset(u.buffers, 0, sizeof(Buffer *) * u.count);
}
else
{
u.textures = new love::graphics::Texture*[u.count];
memset(u.textures, 0, sizeof(Texture *) * u.count);
}
}
}
@@ -257,7 +288,6 @@ void Shader::mapActiveUniforms()
{
if (u.textures[i] == nullptr)
continue;
Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
if (v != nullptr)
v->loadVolatile();
@@ -265,6 +295,19 @@ void Shader::mapActiveUniforms()
sendTextures(&u, u.textures, u.count, true);
}
else if (u.baseType == UNIFORM_TEXELBUFFER)
{
for (int i = 0; i < u.count; i++)
{
if (u.buffers[i] == nullptr)
continue;
Volatile *v = dynamic_cast<Volatile *>(u.buffers[i]);
if (v != nullptr)
v->loadVolatile();
}
sendBuffers(&u, u.buffers, u.count, true);
}
}
// Make sure uniforms that existed before but don't exist anymore are
@@ -275,16 +318,26 @@ void Shader::mapActiveUniforms()
{
free(p.second.data);
if (p.second.baseType != UNIFORM_SAMPLER)
continue;
for (int i = 0; i < p.second.count; i++)
if (p.second.baseType == UNIFORM_SAMPLER)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
for (int i = 0; i < p.second.count; i++)
{
if (p.second.textures[i] != nullptr)
p.second.textures[i]->release();
}
delete[] p.second.textures;
delete[] p.second.textures;
}
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
{
for (int i = 0; i < p.second.count; i++)
{
if (p.second.buffers[i] != nullptr)
p.second.buffers[i]->release();
}
delete[] p.second.buffers;
}
}
}
@@ -322,7 +375,7 @@ bool Shader::loadVolatile()
for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
{
const char *name = nullptr;
if (vertex::getConstant((BuiltinVertexAttribute) i, name))
if (graphics::getConstant((BuiltinVertexAttribute) i, name))
glBindAttribLocation(program, i, (const GLchar *) name);
}
@@ -430,7 +483,12 @@ void Shader::attach()
{
const TextureUnit &unit = textureUnits[i];
if (unit.active)
gl.bindTextureToUnit(unit.type, unit.texture, i, false);
{
if (unit.isTexelBuffer)
gl.bindBufferTextureToUnit(unit.texture, i, false, false);
else
gl.bindTextureToUnit(unit.type, unit.texture, i, false, false);
}
}
// send any pending uniforms to the shader program.
@@ -493,7 +551,7 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda
break;
}
}
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER)
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER || type == UNIFORM_TEXELBUFFER)
{
switch (info->components)
{
@@ -627,7 +685,110 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
int texunit = info->ints[i];
if (shaderactive)
gl.bindTextureToUnit(info->textureType, gltex, texunit, false);
gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
// Store texture id so it can be re-bound to the texture unit later.
textureUnits[texunit].texture = gltex;
}
}
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
{
Shader::sendBuffers(info, buffers, count, false);
}
static bool isTexelBufferTypeCompatible(DataBaseType a, DataBaseType b)
{
if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM)
return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM;
if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT)
return true;
if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT)
return true;
return false;
}
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalUpdate)
{
if (info->baseType != UNIFORM_TEXELBUFFER)
return;
uint32 requiredtypeflags = Buffer::TYPEFLAG_TEXEL;
bool shaderactive = current == this;
if (!internalUpdate && shaderactive)
flushBatchedDraws();
count = std::min(count, info->count);
// Bind the textures to the texture units.
for (int i = 0; i < count; i++)
{
love::graphics::Buffer *buffer = buffers[i];
if (buffer != nullptr)
{
if ((buffer->getTypeFlags() & requiredtypeflags) == 0)
{
if (internalUpdate)
continue;
else
throw love::Exception("Shader uniform '%s' is a texel buffer, but the given Buffer was not created with texel buffer capabilities.", info->name.c_str());
}
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
{
if (internalUpdate)
continue;
else
throw love::Exception("Texel buffer's data format base type must match the variable declared in the shader.");
}
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] = buffer;
GLuint gltex = 0;
if (buffers[i] != nullptr)
gltex = (GLuint) buffer->getTexelBufferHandle();
else
gltex = gl.getDefaultTexelBuffer();
int texunit = info->ints[i];
if (shaderactive)
gl.bindBufferTextureToUnit(gltex, texunit, false, false);
// Store texture id so it can be re-bound to the texture unit later.
textureUnits[texunit].texture = gltex;
@@ -746,11 +907,20 @@ 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
{
if (getUniformBaseType(type) == UNIFORM_SAMPLER)
UniformType basetype = getUniformBaseType(type);
if (basetype == UNIFORM_SAMPLER || basetype == UNIFORM_TEXELBUFFER)
return 1;
switch (type)
@@ -879,6 +1049,10 @@ Shader::UniformType Shader::getUniformBaseType(GLenum type) const
case GL_SAMPLER_CUBE_MAP_ARRAY:
case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
return UNIFORM_SAMPLER;
case GL_SAMPLER_BUFFER:
case GL_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
return UNIFORM_TEXELBUFFER;
default:
return UNIFORM_UNKNOWN;
}
@@ -922,6 +1096,21 @@ TextureType Shader::getUniformTextureType(GLenum type) const
}
}
DataBaseType Shader::getUniformTexelBufferType(GLenum type) const
{
switch (type)
{
case GL_SAMPLER_BUFFER:
return DATA_BASETYPE_FLOAT;
case GL_INT_SAMPLER_BUFFER:
return DATA_BASETYPE_INT;
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
return DATA_BASETYPE_UINT;
default:
return DATA_BASETYPE_MAX_ENUM;
}
}
bool Shader::isDepthTextureType(GLenum type) const
{
switch (type)
+6
View File
@@ -62,6 +62,7 @@ public:
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
void updateUniform(const UniformInfo *info, int count) override;
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override;
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
bool hasUniform(const std::string &name) const override;
ptrdiff_t getHandle() const override;
void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;
@@ -75,6 +76,7 @@ private:
{
GLuint texture = 0;
TextureType type = TEXTURE_2D;
bool isTexelBuffer = false;
bool active = false;
};
@@ -83,11 +85,13 @@ private:
void updateUniform(const UniformInfo *info, int count, bool internalupdate);
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalupdate);
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalupdate);
int getUniformTypeComponents(GLenum type) const;
MatrixSize getMatrixSize(GLenum type) const;
UniformType getUniformBaseType(GLenum type) const;
TextureType getUniformTextureType(GLenum type) const;
DataBaseType getUniformTexelBufferType(GLenum type) const;
bool isDepthTextureType(GLenum type) const;
void flushBatchedDraws() const;
@@ -112,6 +116,8 @@ private:
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
std::vector<Buffer *> buffersToUnmap;
float lastPointSize;
}; // Shader
+10
View File
@@ -42,6 +42,11 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
glGenFramebuffers(1, &framebuffer);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer);
// Intel driver bug: https://github.com/love2d/love/issues/1592
bool current_srgb = gl.isStateEnabled(OpenGL::ENABLE_FRAMEBUFFER_SRGB);
if (current_srgb && isPixelFormatDepthStencil(format))
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, false);
if (texture != 0)
{
if (isPixelFormatDepthStencil(format) && (GLAD_ES_VERSION_3_0 || !GLAD_ES_VERSION_2_0))
@@ -103,6 +108,11 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
// Restore sRGB state if we turned it off above.
if (current_srgb && isPixelFormatDepthStencil(format))
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, current_srgb);
return status;
}