mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Merge remote-tracking branch 'origin/12.0-development' into vulkan
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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];
|
||||
}}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"} } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {}; }
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user