mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Initial support for writing to textures in compute shaders.
This commit is contained in:
@@ -768,7 +768,7 @@ void Graphics::setRenderTargets(const RenderTargets &rts)
|
||||
PixelFormat dsformat = PIXELFORMAT_STENCIL8;
|
||||
if (wantsdepth && wantsstencil)
|
||||
dsformat = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
|
||||
else if (wantsdepth && isPixelFormatSupported(PIXELFORMAT_DEPTH24_UNORM, true, false, false))
|
||||
else if (wantsdepth && isPixelFormatSupported(PIXELFORMAT_DEPTH24_UNORM, PIXELFORMATUSAGEFLAGS_RENDERTARGET, false))
|
||||
dsformat = PIXELFORMAT_DEPTH24_UNORM;
|
||||
else if (wantsdepth)
|
||||
dsformat = PIXELFORMAT_DEPTH16_UNORM;
|
||||
|
||||
@@ -787,9 +787,9 @@ public:
|
||||
virtual PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const = 0;
|
||||
|
||||
/**
|
||||
* Gets whether the specified pixel format is supported.
|
||||
* Gets whether the specified pixel format usage is supported.
|
||||
**/
|
||||
virtual bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) = 0;
|
||||
virtual bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) = 0;
|
||||
|
||||
/**
|
||||
* Gets the renderer used by love.graphics.
|
||||
|
||||
@@ -731,6 +731,55 @@ bool Shader::validate(StrongRef<ShaderStage> stages[], std::string& err)
|
||||
return validateInternal(stages, err, reflection);
|
||||
}
|
||||
|
||||
static PixelFormat getPixelFormat(glslang::TLayoutFormat format)
|
||||
{
|
||||
using namespace glslang;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case ElfNone: return PIXELFORMAT_UNKNOWN;
|
||||
case ElfRgba32f: return PIXELFORMAT_RGBA32_FLOAT;
|
||||
case ElfRgba16f: return PIXELFORMAT_RGBA16_FLOAT;
|
||||
case ElfR32f: return PIXELFORMAT_R32_FLOAT;
|
||||
case ElfRgba8: return PIXELFORMAT_RGBA8_UNORM;
|
||||
case ElfRgba8Snorm: return PIXELFORMAT_UNKNOWN; // no snorm yet
|
||||
case ElfRg32f: return PIXELFORMAT_RG32_FLOAT;
|
||||
case ElfRg16f: return PIXELFORMAT_RG16_FLOAT;
|
||||
case ElfR11fG11fB10f: return PIXELFORMAT_RG11B10_FLOAT;
|
||||
case ElfR16f: return PIXELFORMAT_R16_FLOAT;
|
||||
case ElfRgba16: return PIXELFORMAT_RGBA16_UNORM;
|
||||
case ElfRgb10A2: return PIXELFORMAT_RGB10A2_UNORM;
|
||||
case ElfRg16: return PIXELFORMAT_RG16_UNORM;
|
||||
case ElfRg8: return PIXELFORMAT_RG8_UNORM;
|
||||
case ElfR8: return PIXELFORMAT_R8_UNORM;
|
||||
case ElfRgba16Snorm: return PIXELFORMAT_UNKNOWN;
|
||||
case ElfRg16Snorm: return PIXELFORMAT_UNKNOWN;
|
||||
case ElfRg8Snorm: return PIXELFORMAT_UNKNOWN;
|
||||
case ElfR16Snorm: return PIXELFORMAT_UNKNOWN;
|
||||
case ElfR8Snorm: return PIXELFORMAT_UNKNOWN;
|
||||
case ElfRgba32i: return PIXELFORMAT_RGBA32_INT;
|
||||
case ElfRgba16i: return PIXELFORMAT_RGBA16_INT;
|
||||
case ElfRgba8i: return PIXELFORMAT_RGBA8_INT;
|
||||
case ElfR32i: return PIXELFORMAT_R32_INT;
|
||||
case ElfRg32i: return PIXELFORMAT_RG32_INT;
|
||||
case ElfRg16i: return PIXELFORMAT_RG16_INT;
|
||||
case ElfRg8i: return PIXELFORMAT_RG8_INT;
|
||||
case ElfR16i: return PIXELFORMAT_R16_INT;
|
||||
case ElfR8i: return PIXELFORMAT_R8_INT;
|
||||
case ElfRgba32ui: return PIXELFORMAT_RGBA32_UINT;
|
||||
case ElfRgba16ui: return PIXELFORMAT_RGBA16_UINT;
|
||||
case ElfRgba8ui: return PIXELFORMAT_RGBA8_UINT;
|
||||
case ElfR32ui: return PIXELFORMAT_R32_UINT;
|
||||
case ElfRg32ui: return PIXELFORMAT_RG32_UINT;
|
||||
case ElfRg16ui: return PIXELFORMAT_RG16_UINT;
|
||||
case ElfRgb10a2ui: return PIXELFORMAT_UNKNOWN;
|
||||
case ElfRg8ui: return PIXELFORMAT_RG8_UINT;
|
||||
case ElfR16ui: return PIXELFORMAT_R16_UINT;
|
||||
case ElfR8ui: return PIXELFORMAT_R8_UINT;
|
||||
default: return PIXELFORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err, ValidationReflection &reflection)
|
||||
{
|
||||
glslang::TProgram program;
|
||||
@@ -781,12 +830,34 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
if (type == nullptr)
|
||||
continue;
|
||||
|
||||
const glslang::TQualifier &qualifiers = type->getQualifier();
|
||||
|
||||
if (type->isImage())
|
||||
{
|
||||
// TODO: Change this check to only error if a writable image is
|
||||
// declared in non-compute, once support is added for them.
|
||||
err = "Shader validation error:\nImage load/store (image2D uniforms, etc.) is not supported yet.";
|
||||
return false;
|
||||
if ((info.stages & EShLangComputeMask) == 0)
|
||||
{
|
||||
err = "Shader validation error:\nStorage Texture uniform variables (image2D, etc) are only allowed in compute shaders.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!qualifiers.hasFormat())
|
||||
{
|
||||
err = "Shader validation error:\nStorage Texture '" + info.name + "' must have an explicit format set in its layout declaration.";
|
||||
return false;
|
||||
}
|
||||
|
||||
StorageTextureReflection texreflection = {};
|
||||
|
||||
texreflection.format = getPixelFormat(qualifiers.getFormat());
|
||||
|
||||
if (qualifiers.isReadOnly())
|
||||
texreflection.access = ACCESS_READ;
|
||||
else if (qualifiers.isWriteOnly())
|
||||
texreflection.access = ACCESS_WRITE;
|
||||
else
|
||||
texreflection.access = (Access)(ACCESS_READ | ACCESS_WRITE);
|
||||
|
||||
reflection.storageTextures[info.name] = texreflection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -798,7 +869,7 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
{
|
||||
const glslang::TQualifier &qualifiers = type->getQualifier();
|
||||
|
||||
if ((!qualifiers.isReadOnly() || qualifiers.isWriteOnly()) && (info.stages & (EShLangVertexMask | EShLangFragmentMask)))
|
||||
if ((!qualifiers.isReadOnly() || qualifiers.isWriteOnly()) && (info.stages & EShLangComputeMask) == 0)
|
||||
{
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must be marked as readonly in vertex and pixel shaders.";
|
||||
return false;
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
UNIFORM_UINT,
|
||||
UNIFORM_BOOL,
|
||||
UNIFORM_SAMPLER,
|
||||
UNIFORM_STORAGETEXTURE,
|
||||
UNIFORM_TEXELBUFFER,
|
||||
UNIFORM_STORAGEBUFFER,
|
||||
UNIFORM_UNKNOWN,
|
||||
@@ -135,6 +136,7 @@ public:
|
||||
TextureType textureType;
|
||||
Access access;
|
||||
bool isDepthSampler;
|
||||
PixelFormat storageTextureFormat;
|
||||
size_t bufferStride;
|
||||
size_t bufferMemberCount;
|
||||
std::string name;
|
||||
@@ -251,9 +253,16 @@ protected:
|
||||
Access access;
|
||||
};
|
||||
|
||||
struct StorageTextureReflection
|
||||
{
|
||||
PixelFormat format;
|
||||
Access access;
|
||||
};
|
||||
|
||||
struct ValidationReflection
|
||||
{
|
||||
std::map<std::string, BufferReflection> storageBuffers;
|
||||
std::map<std::string, StorageTextureReflection> storageTextures;
|
||||
int localThreadgroupSize[3];
|
||||
bool usesPointSize;
|
||||
};
|
||||
|
||||
@@ -163,6 +163,7 @@ Texture::Texture(const Settings &settings, const Slices *slices)
|
||||
: texType(settings.type)
|
||||
, format(settings.format)
|
||||
, renderTarget(settings.renderTarget)
|
||||
, computeWrite(settings.computeWrite)
|
||||
, readable(true)
|
||||
, mipmapsMode(settings.mipmaps)
|
||||
, sRGB(isGammaCorrect() && !settings.linear)
|
||||
@@ -257,7 +258,15 @@ Texture::Texture(const Settings &settings, const Slices *slices)
|
||||
if (isCompressed() && renderTarget)
|
||||
throw love::Exception("Compressed textures cannot be render targets.");
|
||||
|
||||
if (!gfx->isPixelFormatSupported(format, renderTarget, readable, sRGB))
|
||||
uint32 usage = PIXELFORMATUSAGEFLAGS_NONE;
|
||||
if (renderTarget)
|
||||
usage |= PIXELFORMATUSAGEFLAGS_RENDERTARGET;
|
||||
if (readable)
|
||||
usage |= PIXELFORMATUSAGEFLAGS_SAMPLE;
|
||||
if (computeWrite)
|
||||
usage |= PIXELFORMATUSAGEFLAGS_COMPUTEWRITE;
|
||||
|
||||
if (!gfx->isPixelFormatSupported(format, (PixelFormatUsageFlags) usage, sRGB))
|
||||
{
|
||||
const char *fstr = "unknown";
|
||||
love::getConstant(format, fstr);
|
||||
@@ -267,7 +276,9 @@ Texture::Texture(const Settings &settings, const Slices *slices)
|
||||
readablestr = readable ? " readable" : " non-readable";
|
||||
|
||||
const char *rtstr = "";
|
||||
if (renderTarget)
|
||||
if (computeWrite)
|
||||
rtstr = " as a compute shader-writable texture";
|
||||
else if (renderTarget)
|
||||
rtstr = " as a render target";
|
||||
|
||||
throw love::Exception("The %s%s pixel format is not supported%s on this system.", fstr, readablestr, rtstr);
|
||||
@@ -571,6 +582,11 @@ bool Texture::isRenderTarget() const
|
||||
return renderTarget;
|
||||
}
|
||||
|
||||
bool Texture::isComputeWritable() const
|
||||
{
|
||||
return computeWrite;
|
||||
}
|
||||
|
||||
bool Texture::isReadable() const
|
||||
{
|
||||
return readable;
|
||||
@@ -934,6 +950,7 @@ static StringMap<Texture::SettingType, Texture::SETTING_MAX_ENUM>::Entry setting
|
||||
{ "dpiscale", Texture::SETTING_DPI_SCALE },
|
||||
{ "msaa", Texture::SETTING_MSAA },
|
||||
{ "canvas", Texture::SETTING_RENDER_TARGET },
|
||||
{ "computewrite", Texture::SETTING_COMPUTE_WRITE },
|
||||
{ "readable", Texture::SETTING_READABLE },
|
||||
};
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ enum PixelFormatUsage
|
||||
PIXELFORMATUSAGE_RENDERTARGET,
|
||||
PIXELFORMATUSAGE_BLEND,
|
||||
PIXELFORMATUSAGE_MSAA,
|
||||
PIXELFORMATUSAGE_COMPUTEWRITE,
|
||||
PIXELFORMATUSAGE_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -74,6 +75,7 @@ enum PixelFormatUsageFlags
|
||||
PIXELFORMATUSAGEFLAGS_RENDERTARGET = (1 << PIXELFORMATUSAGE_RENDERTARGET),
|
||||
PIXELFORMATUSAGEFLAGS_BLEND = (1 << PIXELFORMATUSAGE_BLEND),
|
||||
PIXELFORMATUSAGEFLAGS_MSAA = (1 << PIXELFORMATUSAGE_MSAA),
|
||||
PIXELFORMATUSAGEFLAGS_COMPUTEWRITE = (1 << PIXELFORMATUSAGE_COMPUTEWRITE),
|
||||
};
|
||||
|
||||
struct SamplerState
|
||||
@@ -169,6 +171,7 @@ public:
|
||||
SETTING_DPI_SCALE,
|
||||
SETTING_MSAA,
|
||||
SETTING_RENDER_TARGET,
|
||||
SETTING_COMPUTE_WRITE,
|
||||
SETTING_READABLE,
|
||||
SETTING_MAX_ENUM
|
||||
};
|
||||
@@ -186,6 +189,7 @@ public:
|
||||
float dpiScale = 1.0f;
|
||||
int msaa = 1;
|
||||
bool renderTarget = false;
|
||||
bool computeWrite = false;
|
||||
OptionalBool readable;
|
||||
};
|
||||
|
||||
@@ -249,6 +253,7 @@ public:
|
||||
MipmapsMode getMipmapsMode() const;
|
||||
|
||||
bool isRenderTarget() const;
|
||||
bool isComputeWritable() const;
|
||||
bool isReadable() const;
|
||||
|
||||
bool isCompressed() const;
|
||||
@@ -307,6 +312,7 @@ protected:
|
||||
|
||||
PixelFormat format;
|
||||
bool renderTarget;
|
||||
bool computeWrite;
|
||||
bool readable;
|
||||
|
||||
MipmapsMode mipmapsMode;
|
||||
|
||||
@@ -501,9 +501,9 @@ static bool computeDispatchBarriers(Shader *shader, GLbitfield &preDispatchBarri
|
||||
postDispatchBarriers |= GL_PIXEL_BUFFER_BARRIER_BIT;
|
||||
}
|
||||
|
||||
for (auto texture : shader->getActiveWritableTextures())
|
||||
for (const auto &binding : shader->getStorageTextureBindings())
|
||||
{
|
||||
if (texture == nullptr)
|
||||
if (binding.texture == nullptr)
|
||||
return false;
|
||||
|
||||
preDispatchBarriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
|
||||
@@ -512,7 +512,7 @@ static bool computeDispatchBarriers(Shader *shader, GLbitfield &preDispatchBarri
|
||||
| GL_TEXTURE_UPDATE_BARRIER_BIT
|
||||
| GL_TEXTURE_FETCH_BARRIER_BIT;
|
||||
|
||||
if (texture->isRenderTarget())
|
||||
if (binding.texture->isRenderTarget())
|
||||
postDispatchBarriers |= GL_FRAMEBUFFER_BARRIER_BIT;
|
||||
}
|
||||
|
||||
@@ -1684,7 +1684,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool
|
||||
}
|
||||
}
|
||||
|
||||
bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB)
|
||||
bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB)
|
||||
{
|
||||
if (sRGB && format == PIXELFORMAT_RGBA8_UNORM)
|
||||
{
|
||||
@@ -1692,22 +1692,20 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
|
||||
sRGB = false;
|
||||
}
|
||||
|
||||
uint32 requiredflags = 0;
|
||||
if (rendertarget)
|
||||
requiredflags |= PIXELFORMATUSAGEFLAGS_RENDERTARGET;
|
||||
if (readable)
|
||||
requiredflags |= PIXELFORMATUSAGEFLAGS_SAMPLE;
|
||||
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][sRGB ? 1 : 0];
|
||||
OptionalBool &supported = supportedFormats[format][rendertarget ? 1 : 0][readable ? 1 : 0][computewrite ? 1 : 0][sRGB ? 1 : 0];
|
||||
|
||||
if (supported.hasValue)
|
||||
return supported.value;
|
||||
|
||||
auto supportedflags = OpenGL::getPixelFormatUsageFlags(format);
|
||||
uint32 supportedflags = OpenGL::getPixelFormatUsageFlags(format);
|
||||
|
||||
if ((requiredflags & supportedflags) != requiredflags)
|
||||
if ((usage & supportedflags) != usage)
|
||||
{
|
||||
supported.set(false);
|
||||
return supported.value;
|
||||
|
||||
@@ -107,7 +107,7 @@ public:
|
||||
void setWireframe(bool enable) override;
|
||||
|
||||
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override;
|
||||
bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) override;
|
||||
bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override;
|
||||
Renderer getRenderer() const override;
|
||||
RendererInfo getRendererInfo() const override;
|
||||
|
||||
@@ -170,8 +170,8 @@ private:
|
||||
// Only needed for buffer types that can be bound to shaders.
|
||||
StrongRef<love::graphics::Buffer> defaultBuffers[BUFFERUSAGE_MAX_ENUM];
|
||||
|
||||
// [rendertarget][readable][srgb]
|
||||
OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2];
|
||||
// [rendertarget][readable][computewrite][srgb]
|
||||
OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2][2];
|
||||
|
||||
}; // Graphics
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ void OpenGL::setupContext()
|
||||
// This can't be done in initContext with the rest of the bug checks because
|
||||
// isPixelFormatSupported relies on state initialized here / after init.
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
if (GLAD_ES_VERSION_3_0 && gfx != nullptr && !gfx->isPixelFormatSupported(PIXELFORMAT_R8_UNORM, true, true))
|
||||
if (GLAD_ES_VERSION_3_0 && gfx != nullptr && !gfx->isPixelFormatSupported(PIXELFORMAT_R8_UNORM, PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET))
|
||||
bugs.brokenR8PixelFormat = true;
|
||||
#endif
|
||||
}
|
||||
@@ -2085,6 +2085,7 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
{
|
||||
const uint32 commonsample = PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_LINEAR;
|
||||
const uint32 commonrender = PIXELFORMATUSAGEFLAGS_RENDERTARGET | PIXELFORMATUSAGEFLAGS_BLEND | PIXELFORMATUSAGEFLAGS_MSAA;
|
||||
const uint32 computewrite = PIXELFORMATUSAGEFLAGS_COMPUTEWRITE;
|
||||
|
||||
uint32 flags = PIXELFORMATUSAGEFLAGS_NONE;
|
||||
|
||||
@@ -2096,11 +2097,15 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= commonsample | commonrender;
|
||||
else if (pixelformat == PIXELFORMAT_R8_UNORM && (GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_1))
|
||||
flags |= commonsample; // We'll use OpenGL's luminance format internally.
|
||||
if (GLAD_VERSION_4_3)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_RGBA8_UNORM:
|
||||
flags |= commonsample;
|
||||
if (GLAD_VERSION_1_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8)
|
||||
flags |= commonrender;
|
||||
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_sRGBA8_UNORM:
|
||||
if (gl.bugs.brokenSRGB)
|
||||
@@ -2110,6 +2115,8 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0
|
||||
|| ((GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB) && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB)))
|
||||
flags |= commonrender;
|
||||
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_R16_UNORM:
|
||||
case PIXELFORMAT_RG16_UNORM:
|
||||
@@ -2117,10 +2124,14 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
|| (GLAD_VERSION_1_1 && GLAD_ARB_texture_rg)
|
||||
|| (GLAD_EXT_texture_norm16 && (GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_rg)))
|
||||
flags |= commonsample | commonrender;
|
||||
if (GLAD_VERSION_4_3)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_RGBA16_UNORM:
|
||||
if (GLAD_VERSION_1_1 || GLAD_EXT_texture_norm16)
|
||||
flags |= commonsample | commonrender;
|
||||
if (GLAD_VERSION_4_3)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_R16_FLOAT:
|
||||
case PIXELFORMAT_RG16_FLOAT:
|
||||
@@ -2132,6 +2143,8 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= commonrender;
|
||||
if (!(GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear))
|
||||
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
|
||||
if (GLAD_VERSION_4_3)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_RGBA16_FLOAT:
|
||||
if (GLAD_VERSION_3_0 || (GLAD_VERSION_1_0 && GLAD_ARB_texture_float && GLAD_ARB_half_float_pixel))
|
||||
@@ -2142,8 +2155,13 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= commonrender;
|
||||
if (!(GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear))
|
||||
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
|
||||
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_R32_FLOAT:
|
||||
if (GLAD_ES_VERSION_3_1)
|
||||
flags |= computewrite;
|
||||
// Fallthrough.
|
||||
case PIXELFORMAT_RG32_FLOAT:
|
||||
if (GLAD_VERSION_3_0 || (GLAD_VERSION_1_0 && GLAD_ARB_texture_float && GLAD_ARB_texture_rg))
|
||||
flags |= commonsample | commonrender;
|
||||
@@ -2151,6 +2169,8 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= commonsample;
|
||||
if (!(GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear))
|
||||
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
|
||||
if (GLAD_VERSION_4_3)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_RGBA32_FLOAT:
|
||||
if (GLAD_VERSION_3_0 || (GLAD_VERSION_1_0 && GLAD_ARB_texture_float))
|
||||
@@ -2159,6 +2179,8 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= commonsample;
|
||||
if (!(GLAD_VERSION_1_1 || GLAD_OES_texture_float_linear))
|
||||
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
|
||||
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
|
||||
case PIXELFORMAT_R8_INT:
|
||||
@@ -2181,6 +2203,26 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
case PIXELFORMAT_RGBA32_UINT:
|
||||
if (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0)
|
||||
flags |= PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET;
|
||||
if (GLAD_VERSION_4_3)
|
||||
flags |= computewrite;
|
||||
if (GLAD_ES_VERSION_3_1)
|
||||
{
|
||||
switch (pixelformat)
|
||||
{
|
||||
case PIXELFORMAT_RGBA8_INT:
|
||||
case PIXELFORMAT_RGBA8_UINT:
|
||||
case PIXELFORMAT_RGBA16_INT:
|
||||
case PIXELFORMAT_RGBA16_UINT:
|
||||
case PIXELFORMAT_R32_INT:
|
||||
case PIXELFORMAT_R32_UINT:
|
||||
case PIXELFORMAT_RGBA32_INT:
|
||||
case PIXELFORMAT_RGBA32_UINT:
|
||||
flags |= computewrite;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PIXELFORMAT_LA8_UNORM:
|
||||
@@ -2198,12 +2240,16 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
case PIXELFORMAT_RGB10A2_UNORM:
|
||||
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0)
|
||||
flags |= commonsample | commonrender;
|
||||
if (GLAD_VERSION_4_3)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_RG11B10_FLOAT:
|
||||
if (GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_texture_packed_float)
|
||||
flags |= commonsample;
|
||||
if (GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_color_buffer_packed_float)
|
||||
flags |= commonrender;
|
||||
if (GLAD_VERSION_4_3)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
|
||||
case PIXELFORMAT_STENCIL8:
|
||||
|
||||
@@ -63,7 +63,7 @@ Shader::~Shader()
|
||||
if (p.second.data != nullptr)
|
||||
free(p.second.data);
|
||||
|
||||
if (p.second.baseType == UNIFORM_SAMPLER)
|
||||
if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
@@ -158,6 +158,26 @@ void Shader::mapActiveUniforms()
|
||||
for (int i = 0; i < u.count; i++)
|
||||
textureUnits.push_back(unit);
|
||||
}
|
||||
else if (u.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
StorageTextureBinding binding = {};
|
||||
binding.gltexture = gl.getDefaultTexture(u.textureType, u.dataBaseType);
|
||||
binding.type = u.textureType;
|
||||
|
||||
if ((u.access & (ACCESS_READ | ACCESS_WRITE)) != 0)
|
||||
binding.access = GL_READ_WRITE;
|
||||
else if ((u.access & ACCESS_WRITE) != 0)
|
||||
binding.access = GL_WRITE_ONLY;
|
||||
else if ((u.access & ACCESS_READ) != 0)
|
||||
binding.access = GL_READ_ONLY;
|
||||
|
||||
bool sRGB = false;
|
||||
auto fmt = OpenGL::convertPixelFormat(u.storageTextureFormat, false, sRGB);
|
||||
binding.internalFormat = fmt.internalformat;
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
storageTextureBindings.push_back(binding);
|
||||
}
|
||||
|
||||
// Make sure previously set uniform data is preserved, and shader-
|
||||
// initialized values are retrieved.
|
||||
@@ -183,6 +203,7 @@ void Shader::mapActiveUniforms()
|
||||
case UNIFORM_INT:
|
||||
case UNIFORM_BOOL:
|
||||
case UNIFORM_SAMPLER:
|
||||
case UNIFORM_STORAGETEXTURE:
|
||||
case UNIFORM_TEXELBUFFER:
|
||||
u.dataSize = sizeof(int) * u.components * u.count;
|
||||
u.data = malloc(u.dataSize);
|
||||
@@ -226,6 +247,17 @@ void Shader::mapActiveUniforms()
|
||||
memset(u.textures, 0, sizeof(Texture *) * u.count);
|
||||
}
|
||||
}
|
||||
else if (u.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
int startbinding = (int) storageTextureBindings.size() - u.count;
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.ints[i] = startbinding + i;
|
||||
|
||||
glUniform1iv(u.location, u.count, u.ints);
|
||||
|
||||
u.textures = new love::graphics::Texture*[u.count];
|
||||
memset(u.textures, 0, sizeof(Texture *) * u.count);
|
||||
}
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
@@ -277,7 +309,7 @@ void Shader::mapActiveUniforms()
|
||||
if (builtin != BUILTIN_MAX_ENUM)
|
||||
builtinUniformInfo[(int)builtin] = &uniforms[u.name];
|
||||
|
||||
if (u.baseType == UNIFORM_SAMPLER)
|
||||
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
// Make sure all stored textures have their Volatiles loaded before
|
||||
// the sendTextures call, since it calls getHandle().
|
||||
@@ -401,7 +433,7 @@ void Shader::mapActiveUniforms()
|
||||
if (p.second.data != nullptr)
|
||||
free(p.second.data);
|
||||
|
||||
if (p.second.baseType == UNIFORM_SAMPLER)
|
||||
if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
@@ -435,6 +467,8 @@ bool Shader::loadVolatile()
|
||||
textureUnits.clear();
|
||||
textureUnits.push_back(TextureUnit());
|
||||
|
||||
activeStorageBufferBindings.clear();
|
||||
|
||||
storageBufferBindingIndexToActiveBinding.resize(gl.getMaxShaderStorageBufferBindings(), std::make_pair(-1, -1));
|
||||
activeStorageBufferBindings.clear();
|
||||
activeWritableStorageBuffers.clear();
|
||||
@@ -573,7 +607,7 @@ void Shader::attach()
|
||||
// retain/release happens in Graphics::setShader.
|
||||
|
||||
// Make sure all textures are bound to their respective texture units.
|
||||
for (int i = 0; i < (int) textureUnits.size(); ++i)
|
||||
for (int i = 0; i < (int) textureUnits.size(); i++)
|
||||
{
|
||||
const TextureUnit &unit = textureUnits[i];
|
||||
if (unit.active)
|
||||
@@ -585,6 +619,12 @@ void Shader::attach()
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < storageTextureBindings.size(); i++)
|
||||
{
|
||||
const auto &binding = storageTextureBindings[i];
|
||||
glBindImageTexture((GLuint) i, binding.gltexture, 0, GL_TRUE, 0, binding.access, binding.internalFormat);
|
||||
}
|
||||
|
||||
for (auto bufferbinding : activeStorageBufferBindings)
|
||||
gl.bindIndexedBuffer(bufferbinding.buffer, BUFFERUSAGE_SHADER_STORAGE, bufferbinding.bindingindex);
|
||||
|
||||
@@ -648,7 +688,7 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER || type == UNIFORM_TEXELBUFFER)
|
||||
else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER || type == UNIFORM_STORAGETEXTURE || type == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
switch (info->components)
|
||||
{
|
||||
@@ -717,7 +757,10 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
|
||||
|
||||
void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalUpdate)
|
||||
{
|
||||
if (info->baseType != UNIFORM_SAMPLER)
|
||||
bool issampler = info->baseType == UNIFORM_SAMPLER;
|
||||
bool isstoragetex = info->baseType == UNIFORM_STORAGETEXTURE;
|
||||
|
||||
if (!issampler && !isstoragetex)
|
||||
return;
|
||||
|
||||
bool shaderactive = current == this;
|
||||
@@ -770,6 +813,26 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
|
||||
else
|
||||
throw love::Exception("Texture's data format base type must match the uniform variable declared in the shader (float, int, or uint).");
|
||||
}
|
||||
else if (isstoragetex && !tex->isComputeWritable())
|
||||
{
|
||||
if (internalUpdate)
|
||||
continue;
|
||||
else
|
||||
throw love::Exception("Texture must be created with the computewrite flag set to true in order to be used with a storage texture (image2D etc) shader uniform variable.");
|
||||
}
|
||||
else if (isstoragetex && info->storageTextureFormat != getLinearPixelFormat(tex->getPixelFormat()))
|
||||
{
|
||||
if (internalUpdate)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
const char *texpfstr = "unknown";
|
||||
const char *shaderpfstr = "unknown";
|
||||
love::getConstant(getLinearPixelFormat(tex->getPixelFormat()), texpfstr);
|
||||
love::getConstant(info->storageTextureFormat, shaderpfstr);
|
||||
throw love::Exception("Texture's pixel format (%s) must match the shader uniform variable %s's pixel format (%s)", texpfstr, info->name.c_str(), shaderpfstr);
|
||||
}
|
||||
}
|
||||
|
||||
tex->retain();
|
||||
}
|
||||
@@ -779,19 +842,39 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
|
||||
|
||||
info->textures[i] = tex;
|
||||
|
||||
GLuint gltex = 0;
|
||||
if (textures[i] != nullptr)
|
||||
gltex = (GLuint) tex->getHandle();
|
||||
if (isstoragetex)
|
||||
{
|
||||
GLuint gltex = 0;
|
||||
if (tex != nullptr)
|
||||
gltex = (GLuint) tex->getHandle();
|
||||
else
|
||||
gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
|
||||
|
||||
int bindingindex = info->ints[i];
|
||||
auto &binding = storageTextureBindings[bindingindex];
|
||||
|
||||
binding.texture = tex;
|
||||
binding.gltexture = gltex;
|
||||
|
||||
if (shaderactive)
|
||||
glBindImageTexture(bindingindex, binding.gltexture, 0, GL_TRUE, 0, binding.access, binding.internalFormat);
|
||||
}
|
||||
else
|
||||
gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
|
||||
{
|
||||
GLuint gltex = 0;
|
||||
if (textures[i] != nullptr)
|
||||
gltex = (GLuint) tex->getHandle();
|
||||
else
|
||||
gltex = gl.getDefaultTexture(info->textureType, info->dataBaseType);
|
||||
|
||||
int texunit = info->ints[i];
|
||||
int texunit = info->ints[i];
|
||||
|
||||
if (shaderactive)
|
||||
gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
|
||||
if (shaderactive)
|
||||
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;
|
||||
// Store texture id so it can be re-bound to the texture unit later.
|
||||
textureUnits[texunit].texture = gltex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1243,6 +1326,67 @@ void Shader::computeUniformTypeInfo(GLenum type, UniformInfo &u)
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
break;
|
||||
|
||||
case GL_IMAGE_2D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
break;
|
||||
case GL_INT_IMAGE_2D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_IMAGE_2D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
break;
|
||||
case GL_IMAGE_2D_ARRAY:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
break;
|
||||
case GL_INT_IMAGE_2D_ARRAY:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
break;
|
||||
case GL_IMAGE_3D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
break;
|
||||
case GL_INT_IMAGE_3D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_IMAGE_3D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
break;
|
||||
case GL_IMAGE_CUBE:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
break;
|
||||
case GL_INT_IMAGE_CUBE:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_IMAGE_CUBE:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,15 @@ class Shader final : public love::graphics::Shader, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
struct StorageTextureBinding
|
||||
{
|
||||
Texture *texture = nullptr;
|
||||
GLuint gltexture = 0;
|
||||
TextureType type = TEXTURE_2D;
|
||||
GLenum access = GL_READ_ONLY;
|
||||
GLenum internalFormat;
|
||||
};
|
||||
|
||||
Shader(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]);
|
||||
virtual ~Shader();
|
||||
|
||||
@@ -66,7 +75,7 @@ public:
|
||||
void updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW, int viewportH);
|
||||
|
||||
const std::vector<Buffer *> &getActiveWritableStorageBuffers() const { return activeWritableStorageBuffers; }
|
||||
const std::vector<Texture *> &getActiveWritableTextures() const { return activeWritableTextures; }
|
||||
const std::vector<StorageTextureBinding> &getStorageTextureBindings() const { return storageTextureBindings; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -118,11 +127,12 @@ private:
|
||||
// Texture unit pool for setting textures
|
||||
std::vector<TextureUnit> textureUnits;
|
||||
|
||||
std::vector<StorageTextureBinding> storageTextureBindings;
|
||||
|
||||
std::vector<std::pair<int, int>> storageBufferBindingIndexToActiveBinding;
|
||||
std::vector<BufferBinding> activeStorageBufferBindings;
|
||||
|
||||
std::vector<Buffer *> activeWritableStorageBuffers;
|
||||
std::vector<Texture *> activeWritableTextures;
|
||||
|
||||
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
|
||||
|
||||
|
||||
@@ -801,6 +801,8 @@ static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool chec
|
||||
s.linear = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_LINEAR), s.linear);
|
||||
s.msaa = luax_intflag(L, idx, Texture::getConstant(Texture::SETTING_MSAA), s.msaa);
|
||||
|
||||
s.computeWrite = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_COMPUTE_WRITE), s.computeWrite);
|
||||
|
||||
lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_READABLE));
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
s.readable.set(luax_checkboolean(L, -1));
|
||||
@@ -2640,6 +2642,7 @@ int w_getTextureFormats(lua_State *L)
|
||||
|
||||
bool rt = luax_checkboolflag(L, 1, Texture::getConstant(Texture::SETTING_RENDER_TARGET));
|
||||
bool linear = luax_boolflag(L, 1, Texture::getConstant(Texture::SETTING_LINEAR), false);
|
||||
bool computewrite = luax_boolflag(L, 1, Texture::getConstant(Texture::SETTING_COMPUTE_WRITE), false);
|
||||
|
||||
OptionalBool readable;
|
||||
lua_getfield(L, 1, Texture::getConstant(Texture::SETTING_READABLE));
|
||||
@@ -2663,10 +2666,17 @@ int w_getTextureFormats(lua_State *L)
|
||||
if (rt && isPixelFormatDepth(format))
|
||||
continue;
|
||||
|
||||
bool formatReadable = readable.get(!isPixelFormatDepthStencil(format));
|
||||
bool sRGB = isGammaCorrect() && !linear;
|
||||
|
||||
luax_pushboolean(L, instance()->isPixelFormatSupported(format, rt, formatReadable, sRGB));
|
||||
uint32 usage = PIXELFORMATUSAGEFLAGS_NONE;
|
||||
if (rt)
|
||||
usage |= PIXELFORMATUSAGEFLAGS_RENDERTARGET;
|
||||
if (readable.get(!isPixelFormatDepthStencil(format)))
|
||||
usage |= PIXELFORMATUSAGEFLAGS_SAMPLE;
|
||||
if (computewrite)
|
||||
usage |= PIXELFORMATUSAGEFLAGS_COMPUTEWRITE;
|
||||
|
||||
luax_pushboolean(L, instance()->isPixelFormatSupported(format, (PixelFormatUsageFlags) usage, sRGB));
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
@@ -2709,14 +2719,15 @@ int w_getCanvasFormats(lua_State *L)
|
||||
{
|
||||
supported = [](PixelFormat format) -> bool
|
||||
{
|
||||
return instance()->isPixelFormatSupported(format, true, true, false);
|
||||
const uint32 usage = PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET;
|
||||
return instance()->isPixelFormatSupported(format, (PixelFormatUsageFlags) usage, false);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
supported = [](PixelFormat format) -> bool
|
||||
{
|
||||
return instance()->isPixelFormatSupported(format, true, false, false);
|
||||
return instance()->isPixelFormatSupported(format, PIXELFORMATUSAGEFLAGS_RENDERTARGET, false);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2725,7 +2736,10 @@ int w_getCanvasFormats(lua_State *L)
|
||||
supported = [](PixelFormat format) -> bool
|
||||
{
|
||||
bool readable = !isPixelFormatDepthStencil(format);
|
||||
return instance()->isPixelFormatSupported(format, true, readable, false);
|
||||
uint32 usage = PIXELFORMATUSAGEFLAGS_RENDERTARGET;
|
||||
if (readable)
|
||||
usage |= PIXELFORMATUSAGEFLAGS_SAMPLE;
|
||||
return instance()->isPixelFormatSupported(format, (PixelFormatUsageFlags) usage, false);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2738,7 +2752,7 @@ int w_getImageFormats(lua_State *L)
|
||||
|
||||
const auto supported = [](PixelFormat format) -> bool
|
||||
{
|
||||
return instance()->isPixelFormatSupported(format, false, true, false);
|
||||
return instance()->isPixelFormatSupported(format, PIXELFORMATUSAGEFLAGS_SAMPLE, false);
|
||||
};
|
||||
|
||||
const auto ignore = [](PixelFormat format) -> bool
|
||||
|
||||
@@ -317,6 +317,7 @@ static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, co
|
||||
case Shader::UNIFORM_BOOL:
|
||||
return w_Shader_sendBooleans(L, startidx, shader, info);
|
||||
case Shader::UNIFORM_SAMPLER:
|
||||
case Shader::UNIFORM_STORAGETEXTURE:
|
||||
return w_Shader_sendTextures(L, startidx, shader, info);
|
||||
case Shader::UNIFORM_TEXELBUFFER:
|
||||
case Shader::UNIFORM_STORAGEBUFFER:
|
||||
@@ -328,7 +329,8 @@ static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, co
|
||||
|
||||
static int w_Shader_sendData(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors)
|
||||
{
|
||||
if (info->baseType == Shader::UNIFORM_SAMPLER || info->baseType == Shader::UNIFORM_TEXELBUFFER || info->baseType == Shader::UNIFORM_STORAGEBUFFER)
|
||||
if (info->baseType == Shader::UNIFORM_SAMPLER || info->baseType == Shader::UNIFORM_STORAGETEXTURE
|
||||
|| info->baseType == Shader::UNIFORM_TEXELBUFFER || info->baseType == Shader::UNIFORM_STORAGEBUFFER)
|
||||
return luaL_error(L, "Only value types (floats, ints, vectors, matrices, etc) be sent to Shaders via Data objects.");
|
||||
|
||||
math::Transform::MatrixLayout layout = math::Transform::MATRIX_ROW_MAJOR;
|
||||
|
||||
@@ -279,6 +279,20 @@ int w_Texture_getFormat(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Texture_isRenderTarget(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
luax_pushboolean(L, t->isRenderTarget());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Texture_isComputeWritable(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
luax_pushboolean(L, t->isComputeWritable());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Texture_isReadable(lua_State *L)
|
||||
{
|
||||
Texture *t = luax_checktexture(L, 1);
|
||||
@@ -479,6 +493,8 @@ const luaL_Reg w_Texture_functions[] =
|
||||
{ "setWrap", w_Texture_setWrap },
|
||||
{ "getWrap", w_Texture_getWrap },
|
||||
{ "getFormat", w_Texture_getFormat },
|
||||
{ "isRenderTarget", w_Texture_isRenderTarget },
|
||||
{ "isComputeWritable", w_Texture_isComputeWritable },
|
||||
{ "isReadable", w_Texture_isReadable },
|
||||
{ "getMipmapMode", w_Texture_getMipmapMode },
|
||||
{ "getDepthSampleMode", w_Texture_getDepthSampleMode },
|
||||
|
||||
Reference in New Issue
Block a user