Merge remote-tracking branch 'origin/12.0-development' into vulkan

This commit is contained in:
niki
2022-05-06 16:46:39 +02:00
112 changed files with 1615 additions and 1368 deletions
+28 -15
View File
@@ -253,8 +253,10 @@ void Graphics::createQuadIndexBuffer()
Buffer::Settings settings(BUFFERUSAGEFLAG_INDEX, BUFFERDATAUSAGE_STATIC);
quadIndexBuffer = newBuffer(settings, DATAFORMAT_UINT16, nullptr, size, 0);
Buffer::Mapper map(*quadIndexBuffer);
fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.data);
{
Buffer::Mapper map(*quadIndexBuffer);
fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.data);
}
quadIndexBuffer->setImmutable(true);
}
@@ -310,12 +312,18 @@ love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, in
return new ParticleSystem(texture, size);
}
ShaderStage *Graphics::newShaderStage(ShaderStageType stage, const std::string &source, const Shader::SourceInfo &info)
ShaderStage *Graphics::newShaderStage(ShaderStageType stage, const std::string &source, const Shader::CompileOptions &options, const Shader::SourceInfo &info, bool cache)
{
ShaderStage *s = nullptr;
std::string cachekey;
if (!source.empty())
// Never cache if there are custom defines set... because hashing would get
// more complicated/expensive, and there shouldn't be a lot of duplicate
// shader stages with custom defines anyway.
if (!options.defines.empty())
cache = false;
if (cache && !source.empty())
{
data::HashFunction::Value hashvalue;
data::hash(data::HashFunction::FUNCTION_SHA1, source.c_str(), source.size(), hashvalue);
@@ -333,16 +341,16 @@ ShaderStage *Graphics::newShaderStage(ShaderStageType stage, const std::string &
if (s == nullptr)
{
bool glsles = usesGLSLES();
std::string glsl = Shader::createShaderStageCode(this, stage, source, info, glsles, true);
std::string glsl = Shader::createShaderStageCode(this, stage, source, options, info, glsles, true);
s = newShaderStageInternal(stage, cachekey, glsl, glsles);
if (!cachekey.empty())
if (cache && !cachekey.empty())
cachedShaderStages[stage][cachekey] = s;
}
return s;
}
Shader *Graphics::newShader(const std::vector<std::string> &stagessource, bool vulkan)
Shader *Graphics::newShader(const std::vector<std::string> &stagessource, const Shader::CompileOptions &options)
{
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM] = {};
@@ -353,7 +361,7 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource, bool v
for (const std::string &source : stagessource)
{
Shader::SourceInfo info = Shader::getSourceInfo(source);
info.vulkan = vulkan;
info.vulkan = options.defines.find("vulkan") != options.defines.end();
bool isanystage = false;
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
@@ -364,12 +372,12 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource, bool v
if (info.stages[i] != Shader::ENTRYPOINT_NONE)
{
isanystage = true;
stages[i].set(newShaderStage((ShaderStageType) i, source, info), Acquire::NORETAIN);
stages[i].set(newShaderStage((ShaderStageType) i, source, options, info, true), Acquire::NORETAIN);
}
}
if (!isanystage)
throw love::Exception("Could not parse shader code (missing 'position' or 'effect' function?)");
throw love::Exception("Could not parse shader code (missing shader entry point function such as 'position' or 'effect')");
}
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
@@ -379,7 +387,8 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource, bool v
{
const std::string &source = Shader::getDefaultCode(Shader::STANDARD_DEFAULT, stype);
Shader::SourceInfo info = Shader::getSourceInfo(source);
stages[i].set(newShaderStage(stype, source, info), Acquire::NORETAIN);
Shader::CompileOptions opts;
stages[i].set(newShaderStage(stype, source, opts, info, true), Acquire::NORETAIN);
}
}
@@ -387,7 +396,7 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource, bool v
return newShaderInternal(stages);
}
Shader *Graphics::newComputeShader(const std::string &source)
Shader *Graphics::newComputeShader(const std::string &source, const Shader::CompileOptions &options)
{
Shader::SourceInfo info = Shader::getSourceInfo(source);
@@ -395,7 +404,11 @@ Shader *Graphics::newComputeShader(const std::string &source)
throw love::Exception("Could not parse compute shader code (missing 'computemain' function?)");
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM];
stages[SHADERSTAGE_COMPUTE].set(newShaderStage(SHADERSTAGE_COMPUTE, source, info));
// Don't bother caching compute shader intermediate source, since there
// shouldn't be much reuse.
stages[SHADERSTAGE_COMPUTE].set(newShaderStage(SHADERSTAGE_COMPUTE, source, options, info, false));
return newShaderInternal(stages);
}
@@ -430,7 +443,7 @@ void Graphics::cleanupCachedShaderStage(ShaderStageType type, const std::string
cachedShaderStages[type].erase(hashkey);
}
bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagessource, std::string &err)
bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagessource, const Shader::CompileOptions &options, std::string &err)
{
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM] = {};
@@ -456,7 +469,7 @@ bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagess
if (info.stages[i] != Shader::ENTRYPOINT_NONE)
{
isanystage = true;
std::string glsl = Shader::createShaderStageCode(this, stype, source, info, gles, false);
std::string glsl = Shader::createShaderStageCode(this, stype, source, options, info, gles, false);
stages[i].set(new ShaderStageForValidation(this, stype, glsl, gles), Acquire::NORETAIN);
}
}
+5 -5
View File
@@ -449,8 +449,8 @@ public:
SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferDataUsage usage);
ParticleSystem *newParticleSystem(Texture *texture, int size);
Shader *newShader(const std::vector<std::string> &stagessource, bool vulkan = false);
Shader *newComputeShader(const std::string &source);
Shader *newShader(const std::vector<std::string> &stagessource, const Shader::CompileOptions &options);
Shader *newComputeShader(const std::string &source, const Shader::CompileOptions &options);
virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) = 0;
virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength);
@@ -461,7 +461,7 @@ public:
Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
bool validateShader(bool gles, const std::vector<std::string> &stages, std::string &err);
bool validateShader(bool gles, const std::vector<std::string> &stages, const Shader::CompileOptions &options, std::string &err);
/**
* Resets the current color, background color, line style, and so forth.
@@ -798,7 +798,7 @@ public:
/**
* Gets whether the specified pixel format usage is supported.
**/
virtual bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) = 0;
virtual bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) = 0;
/**
* Gets the renderer used by love.graphics.
@@ -966,7 +966,7 @@ protected:
{}
};
ShaderStage *newShaderStage(ShaderStageType stage, const std::string &source, const Shader::SourceInfo &info);
ShaderStage *newShaderStage(ShaderStageType stage, const std::string &source, const Shader::CompileOptions &options, const Shader::SourceInfo &info, bool cache);
virtual ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0;
virtual Shader *newShaderInternal(StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) = 0;
virtual StreamBuffer *newStreamBuffer(BufferUsage type, size_t size) = 0;
+5 -3
View File
@@ -573,7 +573,7 @@ Shader::SourceInfo Shader::getSourceInfo(const std::string &src)
return info;
}
std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const Shader::SourceInfo &info, bool gles, bool checksystemfeatures)
std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const CompileOptions &options, const Shader::SourceInfo &info, bool gles, bool checksystemfeatures)
{
if (info.language == Shader::LANGUAGE_MAX_ENUM)
throw love::Exception("Invalid shader language");
@@ -630,8 +630,10 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage,
ss << "#define LOVE_GAMMA_CORRECT 1\n";
if (info.usesMRT)
ss << "#define LOVE_MULTI_RENDER_TARGETS 1\n";
if (info.vulkan)
ss << "#define USE_VULKAN\n";
for (const auto &def : options.defines)
ss << "#define " + def.first + " " + def.second + "\n";
ss << glsl::global_syntax;
ss << stageinfo.header;
ss << stageinfo.uniforms;
+6 -1
View File
@@ -107,6 +107,11 @@ public:
ACCESS_WRITE = (1 << 1),
};
struct CompileOptions
{
std::map<std::string, std::string> defines;
};
struct SourceInfo
{
Language language;
@@ -237,7 +242,7 @@ public:
void getLocalThreadgroupSize(int *x, int *y, int *z);
static SourceInfo getSourceInfo(const std::string &src);
static std::string createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const SourceInfo &info, bool gles, bool checksystemfeatures);
static std::string createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const CompileOptions &options, const SourceInfo &info, bool gles, bool checksystemfeatures);
static bool validate(StrongRef<ShaderStage> stages[], std::string &err);
+43 -8
View File
@@ -232,8 +232,13 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
if (mipmapsMode != MIPMAPS_NONE)
mipmapCount = getTotalMipmapCount(pixelWidth, pixelHeight, depth);
if (mipmapsMode == MIPMAPS_AUTO && isPixelFormatDepthStencil(format))
throw love::Exception("Automatic mipmap generation cannot be used for depth/stencil textures.");
const char *miperr = nullptr;
if (mipmapsMode == MIPMAPS_AUTO && !supportsGenerateMipmaps(miperr))
{
const char *fstr = "unknown";
love::getConstant(format, fstr);
throw love::Exception("Automatic mipmap generation is not supported for textures with the %s pixel format.", fstr);
}
if (pixelWidth <= 0 || pixelHeight <= 0 || layers <= 0 || depth <= 0)
throw love::Exception("Texture dimensions must be greater than 0.");
@@ -511,19 +516,49 @@ void Texture::replacePixels(const void *data, size_t size, int slice, int mipmap
generateMipmaps();
}
void Texture::generateMipmaps()
bool Texture::supportsGenerateMipmaps(const char *&outReason) const
{
if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE)
throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled.");
if (getMipmapsMode() == MIPMAPS_NONE)
{
outReason = "generateMipmaps can only be called on a Texture which was created with mipmaps enabled.";
return false;
}
if (isPixelFormatCompressed(format))
throw love::Exception("generateMipmaps cannot be called on a compressed Texture.");
{
outReason = "generateMipmaps cannot be called on a compressed Texture.";
return false;
}
if (isPixelFormatDepthStencil(format))
throw love::Exception("generateMipmaps cannot be called on a depth/stencil Texture.");
{
outReason = "generateMipmaps cannot be called on a depth/stencil Texture.";
return false;
}
if (isPixelFormatInteger(format))
throw love::Exception("generateMipmaps cannot be called on an integer Texture.");
{
outReason = "generateMipmaps cannot be called on an integer Texture.";
return false;
}
// This should be linear | rt because that's what metal needs, but the above
// code handles textures can't be used as RTs in metal.
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (gfx != nullptr && !gfx->isPixelFormatSupported(format, PIXELFORMATUSAGEFLAGS_LINEAR))
{
outReason = "generateMipmaps cannot be called on textures with formats that don't support linear filtering on this system.";
return false;
}
return true;
}
void Texture::generateMipmaps()
{
const char *err = nullptr;
if (!supportsGenerateMipmaps(err))
throw love::Exception("%s", err);
generateMipmapsInternal();
}
+1
View File
@@ -311,6 +311,7 @@ protected:
void uploadImageData(love::image::ImageDataBase *d, int level, int slice, int x, int y);
virtual void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) = 0;
bool supportsGenerateMipmaps(const char *&outReason) const;
virtual void generateMipmapsInternal() = 0;
virtual void readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect) = 0;
+2 -2
View File
@@ -111,7 +111,7 @@ Buffer::~Buffer()
void *Buffer::map(MapType /*map*/, size_t offset, size_t size)
{ @autoreleasepool {
if (size == 0)
if (size == 0 || isImmutable())
return nullptr;
Range r(offset, size);
@@ -160,7 +160,7 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize)
void Buffer::fill(size_t offset, size_t size, const void *data)
{ @autoreleasepool {
if (size == 0)
if (size == 0 || isImmutable())
return;
size_t buffersize = getSize();
+1 -1
View File
@@ -110,7 +110,7 @@ public:
void setWireframe(bool enable) override;
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override;
bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override;
bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override;
Renderer getRenderer() const override;
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
+3 -7
View File
@@ -130,11 +130,6 @@ static inline id<MTLTexture> getMTLTexture(love::graphics::Texture *tex)
return tex ? (__bridge id<MTLTexture>)(void *) tex->getHandle() : nil;
}
static inline id<MTLSamplerState> getMTLSampler(love::graphics::Texture *tex)
{
return tex ? (__bridge id<MTLSamplerState>)(void *) tex->getSamplerHandle() : nil;
}
static inline id<MTLTexture> getMTLRenderTarget(love::graphics::Texture *tex)
{
return tex ? (__bridge id<MTLTexture>)(void *) tex->getRenderTargetHandle() : nil;
@@ -348,9 +343,10 @@ Graphics::Graphics()
if (!Shader::standardShaders[i])
{
std::vector<std::string> stages;
Shader::CompileOptions opts;
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX));
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL));
Shader::standardShaders[i] = newShader(stages);
Shader::standardShaders[i] = newShader(stages, opts);
}
}
@@ -1782,7 +1778,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool /*rendertarget*/,
}
}
bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB)
bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB)
{
bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0;
bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0;
+6 -5
View File
@@ -232,11 +232,6 @@ static inline id<MTLTexture> getMTLTexture(love::graphics::Buffer *buffer)
return buffer ? (__bridge id<MTLTexture>)(void *) buffer->getTexelBufferHandle() : nil;
}
static inline id<MTLSamplerState> getMTLSampler(love::graphics::Texture *tex)
{
return tex ? (__bridge id<MTLSamplerState>)(void *) tex->getSamplerHandle() : nil;
}
static inline id<MTLBuffer> getMTLBuffer(love::graphics::Buffer *buffer)
{
return buffer ? (__bridge id<MTLBuffer>)(void *) buffer->getHandle() : nil;
@@ -313,6 +308,12 @@ Shader::Shader(id<MTLDevice> device, StrongRef<love::graphics::ShaderStage> stag
bool forcedefault = false;
bool forwardcompat = true;
#ifdef LOVE_IOS
defaultversion = 320;
defaultprofile = EEsProfile;
forcedefault = true;
#endif
if (!tshader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
{
const char *stagename = "unknown";
-2
View File
@@ -269,8 +269,6 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
void Texture::generateMipmapsInternal()
{ @autoreleasepool {
// TODO: alternate method for non-color-renderable and non-filterable
// pixel formats.
id<MTLBlitCommandEncoder> encoder = Graphics::getInstance()->useBlitEncoder();
[encoder generateMipmapsForTexture:texture];
}}
+75 -81
View File
@@ -112,7 +112,7 @@ Graphics::Graphics()
, bufferMapMemory(nullptr)
, bufferMapMemorySize(2 * 1024 * 1024)
, defaultBuffers()
, supportedFormats()
, pixelFormatUsage()
{
gl = OpenGL();
@@ -425,9 +425,10 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
if (!Shader::standardShaders[i])
{
std::vector<std::string> stages;
Shader::CompileOptions opts;
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX));
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL));
Shader::standardShaders[i] = newShader(stages);
Shader::standardShaders[i] = newShader(stages, opts);
}
}
catch (love::Exception &)
@@ -1669,6 +1670,13 @@ void Graphics::initCapabilities()
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
for (int i = 0; i < PIXELFORMAT_MAX_ENUM; i++)
{
auto format = (PixelFormat) i;
pixelFormatUsage[i][0] = computePixelFormatUsage(format, false);
pixelFormatUsage[i][1] = computePixelFormatUsage(format, true);
}
}
PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const
@@ -1696,111 +1704,97 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool
}
}
bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB)
uint32 Graphics::computePixelFormatUsage(PixelFormat format, bool readable)
{
if (sRGB)
{
format = getSRGBPixelFormat(format);
sRGB = false;
}
uint32 usage = OpenGL::getPixelFormatUsageFlags(format);
bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0;
bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0;
bool computewrite = (usage & PIXELFORMATUSAGEFLAGS_COMPUTEWRITE) != 0;
format = getSizedFormat(format, rendertarget, readable);
OptionalBool &supported = supportedFormats[format][rendertarget ? 1 : 0][readable ? 1 : 0][computewrite ? 1 : 0][sRGB ? 1 : 0];
if (supported.hasValue)
return supported.value;
uint32 supportedflags = OpenGL::getPixelFormatUsageFlags(format);
if ((usage & supportedflags) != usage)
{
supported.set(false);
return supported.value;
}
if (!rendertarget)
{
supported.set(true);
return supported.value;
}
if (readable && (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) == 0)
return 0;
// Even though we might have the necessary OpenGL version or extension,
// drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching
// a texture to a FBO whose format the driver doesn't like. So we should
// test with an actual FBO.
GLuint texture = 0;
GLuint renderbuffer = 0;
// Avoid the test for depth/stencil formats - not every GL version
// guarantees support for depth/stencil-only render targets (which we would
// need for the test below to work), and we already do some finagling in
// convertPixelFormat to try to use the best-supported internal
// depth/stencil format for a particular driver.
if (isPixelFormatDepthStencil(format))
if ((usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0 && !isPixelFormatDepthStencil(format))
{
supported.set(true);
return true;
}
GLuint texture = 0;
GLuint renderbuffer = 0;
bool sRGB = isPixelFormatSRGB(format);
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, !readable, sRGB);
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, !readable, sRGB);
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
// Make sure at least something is bound to a color attachment. I believe
// this is required on ES2 but I'm not positive.
if (isPixelFormatDepthStencil(format))
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT), 0, 0, 0);
if (readable)
{
glGenTextures(1, &texture);
gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
SamplerState s;
s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST;
gl.setSamplerState(TEXTURE_2D, s);
gl.rawTexStorage(TEXTURE_2D, 1, format, sRGB, 1, 1);
}
else
{
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1);
}
for (GLenum attachment : fmt.framebufferAttachments)
{
if (attachment == GL_NONE)
continue;
// Make sure at least something is bound to a color attachment. I believe
// this is required on ES2 but I'm not positive.
if (isPixelFormatDepthStencil(format))
gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT), 0, 0, 0);
if (readable)
gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0);
{
glGenTextures(1, &texture);
gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
SamplerState s;
s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST;
gl.setSamplerState(TEXTURE_2D, s);
gl.rawTexStorage(TEXTURE_2D, 1, format, sRGB, 1, 1);
}
else
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer);
{
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1);
}
for (GLenum attachment : fmt.framebufferAttachments)
{
if (attachment == GL_NONE)
continue;
if (readable)
gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0);
else
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer);
}
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
usage &= ~PIXELFORMATUSAGEFLAGS_RENDERTARGET;
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
gl.deleteFramebuffer(fbo);
if (texture != 0)
gl.deleteTexture(texture);
if (renderbuffer != 0)
glDeleteRenderbuffers(1, &renderbuffer);
}
supported.set(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
return usage;
}
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
gl.deleteFramebuffer(fbo);
bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB)
{
if (sRGB)
format = getSRGBPixelFormat(format);
if (texture != 0)
gl.deleteTexture(texture);
bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0;
bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0;
if (renderbuffer != 0)
glDeleteRenderbuffers(1, &renderbuffer);
format = getSizedFormat(format, rendertarget, readable);
return supported.value;
return (usage & pixelFormatUsage[format][readable ? 1 : 0]) == usage;
}
} // opengl
+5 -3
View File
@@ -106,7 +106,7 @@ public:
void setWireframe(bool enable) override;
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override;
bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override;
bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override;
Renderer getRenderer() const override;
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
@@ -155,6 +155,8 @@ private:
void setDebug(bool enable);
uint32 computePixelFormatUsage(PixelFormat format, bool readable);
std::unordered_map<RenderTargets, GLuint, CachedFBOHasher> framebufferObjects;
bool windowHasStencil;
GLuint mainVAO;
@@ -170,8 +172,8 @@ private:
// Only needed for buffer types that can be bound to shaders.
StrongRef<love::graphics::Buffer> defaultBuffers[BUFFERUSAGE_MAX_ENUM];
// [rendertarget][readable][computewrite][srgb]
OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2][2];
// [non-readable, readable]
uint32 pixelFormatUsage[PIXELFORMAT_MAX_ENUM][2];
}; // Graphics
+1 -1
View File
@@ -823,7 +823,7 @@ namespace love {
std::vector<std::string> stages;
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX));
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL));
Shader::standardShaders[i] = newShader(stages, true);
Shader::standardShaders[i] = newShader(stages, { { {"vulkan", "1"} } });
}
}
}
+1 -1
View File
@@ -68,7 +68,7 @@ namespace love {
void setPointSize(float size) override { std::cout << "setPointSize "; }
void setWireframe(bool enable) override { std::cout << "setWireframe "; }
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override { std::cout << "getSizedFormat "; return format; }
bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override { std::cout << "isPixelFormatSupported "; return true; }
bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override { std::cout << "isPixelFormatSupported "; return true; }
Renderer getRenderer() const override { std::cout << "getRenderer "; return RENDERER_VULKAN; }
bool usesGLSLES() const override { std::cout << "usesGLSES "; return false; }
RendererInfo getRendererInfo() const override { std::cout << "getRendererInfo "; return {}; }
+52 -8
View File
@@ -1347,7 +1347,7 @@ int w_newParticleSystem(lua_State *L)
return 1;
}
static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string> &stages)
static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string> &stages, Shader::CompileOptions &options)
{
using namespace love::filesystem;
@@ -1369,7 +1369,6 @@ static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string
lua_replace(L, i);
}
continue;
}
@@ -1412,18 +1411,61 @@ static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string
if (has_arg2)
stages.push_back(luax_checkstring(L, startidx + 1));
int optionsidx = has_arg2 ? startidx + 2 : startidx + 1;
if (!lua_isnoneornil(L, optionsidx))
{
luaL_checktype(L, optionsidx, LUA_TTABLE);
lua_getfield(L, optionsidx, "defines");
if (!lua_isnoneornil(L, -1))
{
if (!lua_istable(L, -1))
luaL_argerror(L, optionsidx, "expected 'defines' field to be a table");
lua_pushnil(L);
while (lua_next(L, -2))
{
std::string defname;
std::string defval;
if (lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TSTRING)
defname = luaL_checkstring(L, -1);
else if (lua_type(L, -2) != LUA_TSTRING)
luaL_argerror(L, optionsidx, "all fields in the 'defines' table must use string keys.");
else
{
defname = luaL_checkstring(L, -2);
if (lua_type(L, -1) == LUA_TBOOLEAN)
defval = luax_toboolean(L, -1) ? "1" : "0";
else
{
const char *val = lua_tostring(L, -1);
if (val == nullptr)
luaL_argerror(L, optionsidx, "'defines' table values must be strings, numbers, or booleans.");
defval = val;
}
}
options.defines[defname] = defval;
lua_pop(L, 1);
}
}
lua_pop(L, 1);
}
return 0;
}
int w_newShader(lua_State *L)
{
std::vector<std::string> stages;
w_getShaderSource(L, 1, stages);
Shader::CompileOptions options;
w_getShaderSource(L, 1, stages, options);
bool should_error = false;
try
{
Shader *shader = instance()->newShader(stages);
Shader *shader = instance()->newShader(stages, options);
luax_pushtype(L, shader);
shader->release();
}
@@ -1446,12 +1488,13 @@ int w_newShader(lua_State *L)
int w_newComputeShader(lua_State* L)
{
std::vector<std::string> stages;
w_getShaderSource(L, 1, stages);
Shader::CompileOptions options;
w_getShaderSource(L, 1, stages, options);
bool should_error = false;
try
{
Shader *shader = instance()->newComputeShader(stages[0]);
Shader *shader = instance()->newComputeShader(stages[0], options);
luax_pushtype(L, shader);
shader->release();
}
@@ -1476,13 +1519,14 @@ int w_validateShader(lua_State *L)
bool gles = luax_checkboolean(L, 1);
std::vector<std::string> stages;
w_getShaderSource(L, 2, stages);
Shader::CompileOptions options;
w_getShaderSource(L, 2, stages, options);
bool success = true;
std::string err;
try
{
success = instance()->validateShader(gles, stages, err);
success = instance()->validateShader(gles, stages, options, err);
}
catch (love::Exception &e)
{
+1 -1
View File
@@ -33,7 +33,7 @@ function love.graphics.newVideo(file, settings)
local source, success
if settings.audio ~= false and love.audio then
success, source = pcall(love.audio.newSource, video:getStream():getFilename(), "stream")
success, source = pcall(love.audio.newSource, video:getStream():getFilename(), "stream", "file")
end
if success then
video:setSource(source)