Add texture views. Fixes #2022.

Add love.graphics.newTextureView(basetexture, viewsettings). Requires GLSL4 support.
Add 'viewformats' boolean setting to texture setting tables.
Add Texture:hasViewFormats.

The viewsettings table has the following fields:
  format = nil, -- set this to a pixel format to override the base format. It must have the same number of bytes per pixel as the original, and the 'viewformats' setting on the original texture must be true for this to work. Cannot be used for compressed or depth/stencil textures.
  type = nil, -- set this to a texture type to override the base texture type. 2d base textures can make [2d, array] views. [array, cube] base textures can make [2d, array, cube] views.
  mipmapstart = nil, -- set to a number to use a less detailed mipmap level as a base.
  mipmapcount = nil, -- set to a number to override the number of mipmaps in the texture view.
  layerstart = nil, -- set to a number to use a specific layer or cube face as a base.
  layers = nil, -- set to a number to override the number of layers in an array texture view.
This commit is contained in:
Sasha Szpakowski
2024-02-24 15:45:43 -04:00
parent 1e97e09b28
commit 42812ad521
20 changed files with 588 additions and 216 deletions
+18 -6
View File
@@ -1203,15 +1203,16 @@ bool Graphics::isRenderTargetActive() const
bool Graphics::isRenderTargetActive(Texture *texture) const
{
Texture *roottexture = texture->getRootViewInfo().texture;
const auto &rts = states.back().renderTargets;
for (const auto &rt : rts.colors)
{
if (rt.texture.get() == texture)
if (rt.texture.get() && rt.texture->getRootViewInfo().texture == roottexture)
return true;
}
if (rts.depthStencil.texture.get() == texture)
if (rts.depthStencil.texture.get() && rts.depthStencil.texture->getRootViewInfo().texture == roottexture)
return true;
return false;
@@ -1219,16 +1220,27 @@ bool Graphics::isRenderTargetActive(Texture *texture) const
bool Graphics::isRenderTargetActive(Texture *texture, int slice) const
{
const auto &rootinfo = texture->getRootViewInfo();
slice += rootinfo.startLayer;
const auto &rts = states.back().renderTargets;
for (const auto &rt : rts.colors)
{
if (rt.texture.get() == texture && rt.slice == slice)
return true;
if (rt.texture.get())
{
const auto &info = rt.texture->getRootViewInfo();
if (rootinfo.texture == info.texture && rt.slice + info.startLayer == slice)
return true;
}
}
if (rts.depthStencil.texture.get() == texture && rts.depthStencil.slice == slice)
return true;
if (rts.depthStencil.texture.get())
{
const auto &info = rts.depthStencil.texture->getRootViewInfo();
if (rootinfo.texture == info.texture && rts.depthStencil.slice + info.startLayer == slice)
return true;
}
return false;
}
+1
View File
@@ -455,6 +455,7 @@ public:
virtual ~Graphics();
virtual Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) = 0;
virtual Texture *newTextureView(Texture *base, const Texture::ViewSettings &viewsettings) = 0;
Quad *newQuad(Quad::Viewport v, double sw, double sh);
Font *newFont(love::font::Rasterizer *data);
+1 -1
View File
@@ -1141,7 +1141,7 @@ bool Shader::validateTexture(const UniformInfo *info, Texture *tex, bool interna
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()))
else if (isstoragetex && info->storageTextureFormat != tex->getPixelFormat())
{
if (internalUpdate)
return false;
+204 -75
View File
@@ -166,6 +166,7 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
, format(settings.format)
, renderTarget(settings.renderTarget)
, computeWrite(settings.computeWrite)
, viewFormats(settings.viewFormats)
, readable(true)
, mipmapsMode(settings.mipmaps)
, width(settings.width)
@@ -179,6 +180,8 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
, samplerState()
, graphicsMemorySize(0)
, debugName(settings.debugName)
, rootView({this, 0, 0})
, parentView({this, 0, 0})
{
const auto &caps = gfx->getCapabilities();
int requestedMipmapCount = settings.mipmapCount;
@@ -284,31 +287,7 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
if (isCompressed() && renderTarget)
throw love::Exception("Compressed textures cannot be render targets.");
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))
{
const char *fstr = "unknown";
love::getConstant(format, fstr);
const char *readablestr = "";
if (readable != !isPixelFormatDepthStencil(format))
readablestr = readable ? " readable" : " non-readable";
const char *rtstr = "";
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);
}
validatePixelFormat(gfx);
if (!caps.textureTypes[texType])
{
@@ -330,10 +309,149 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
++textureCount;
}
Texture::Texture(Graphics *gfx, Texture *base, const ViewSettings &viewsettings)
: texType(viewsettings.type.get(base->getTextureType()))
, format(viewsettings.format.get(base->getPixelFormat()))
, renderTarget(base->renderTarget)
, computeWrite(base->computeWrite)
, viewFormats(base->viewFormats)
, readable(base->readable)
, mipmapsMode(base->mipmapsMode)
, width(1)
, height(1)
, depth(1)
, layers(1)
, mipmapCount(1)
, pixelWidth(1)
, pixelHeight(1)
, requestedMSAA(base->requestedMSAA)
, samplerState(base->samplerState)
, quad(base->quad)
, graphicsMemorySize(0)
, debugName(viewsettings.debugName)
, rootView({base->rootView.texture, 0, 0})
, parentView({base, viewsettings.mipmapStart.get(0), viewsettings.layerStart.get(0)})
{
width = base->getHeight(parentView.startMipmap);
height = base->getHeight(parentView.startMipmap);
if (texType == TEXTURE_VOLUME)
depth = base->getDepth(parentView.startMipmap);
if (texType == TEXTURE_2D_ARRAY)
{
int baselayers = base->getTextureType() == TEXTURE_CUBE ? 6 : base->getLayerCount();
layers = viewsettings.layerCount.get(baselayers - parentView.startLayer);
}
mipmapCount = viewsettings.mipmapCount.get(base->getMipmapCount() - parentView.startMipmap);
pixelWidth = base->getPixelWidth(parentView.startMipmap);
pixelHeight = base->getPixelHeight(parentView.startMipmap);
if (parentView.startMipmap < 0)
throw love::Exception("Invalid mipmap start value for texture view (out of range).");
if (mipmapCount < 0 || parentView.startMipmap + mipmapCount > base->getMipmapCount())
throw love::Exception("Invalid mipmap start or count value for texture view (out of range).");
if (parentView.startLayer < 0)
throw love::Exception("Invalid layer start value for texture view (out of range).");
int baseLayerCount = base->getTextureType() == TEXTURE_CUBE ? 6 : base->getLayerCount();
if (layers < 0 || parentView.startLayer + layers > baseLayerCount)
throw love::Exception("Invalid layer start or count value for texture view (out of range).");
if (texType == TEXTURE_CUBE && parentView.startLayer + 6 > baseLayerCount)
throw love::Exception("Cube texture view cannot fit in the base texture's layers with the given start layer.");
ViewInfo nextView = { this, 0, 0 };
while (nextView.texture != rootView.texture)
{
nextView = nextView.texture->parentView;
rootView.startMipmap += nextView.startMipmap;
rootView.startLayer += nextView.startLayer;
}
const auto &caps = gfx->getCapabilities();
if (!caps.features[Graphics::FEATURE_GLSL4])
throw love::Exception("Texture views are not supported on this system (GLSL 4 support is necessary.)");
validatePixelFormat(gfx);
if (!caps.textureTypes[texType])
{
const char *textypestr = "unknown";
Texture::getConstant(texType, textypestr);
throw love::Exception("%s textures are not supported on this system.", textypestr);
}
if (!readable)
throw love::Exception("Texture views are not supported for non-readable textures.");
if (base->getTextureType() == TEXTURE_2D)
{
if (texType != TEXTURE_2D && texType != TEXTURE_2D_ARRAY)
throw love::Exception("Texture views created from a 2D texture must use the 2d or array texture type.");
}
else if (base->getTextureType() == TEXTURE_2D_ARRAY || base->getTextureType() == TEXTURE_CUBE)
{
if (texType != TEXTURE_2D && texType != TEXTURE_2D_ARRAY && texType != TEXTURE_CUBE)
throw love::Exception("Texture views created from an array or cube texture must use the 2d, array, or cube texture type.");
}
else if (base->getTextureType() == TEXTURE_VOLUME)
{
if (texType != TEXTURE_VOLUME)
throw love::Exception("Texture views created from a volume texture must use the volume texture type.");
}
else
{
throw love::Exception("Unknown texture type.");
}
auto baseformat = base->getPixelFormat();
if (format != baseformat)
{
if (isPixelFormatCompressed(baseformat) || isPixelFormatCompressed(format))
throw love::Exception("Compressed textures cannot use a different pixel format in a texture view.");
if (isPixelFormatColor(baseformat) != isPixelFormatColor(format))
throw love::Exception("Color-format textures cannot use a depth/stencil pixel format and vice versa, in a texture view.");
// TODO: depth[24|32f]_stencil8 -> stencil8 can work.
if (isPixelFormatDepthStencil(baseformat))
throw love::Exception("Using a different pixel format in a texture view is not currently supported for depth or stencil formats.");
if (!viewFormats)
throw love::Exception("Using a different pixel format in a texture view requires the original texture to be created with the 'viewformats' setting set to true.");
size_t bytes = getPixelFormatBlockSize(format);
size_t basebytes = getPixelFormatBlockSize(baseformat);
if (bytes != basebytes)
throw love::Exception("Texture views must have the same bits per pixel as the original texture.");
}
const char *miperr = nullptr;
if (mipmapsMode == MIPMAPS_AUTO && !supportsGenerateMipmaps(miperr))
mipmapsMode = MIPMAPS_MANUAL;
rootView.texture->retain();
parentView.texture->retain();
}
Texture::~Texture()
{
--textureCount;
setGraphicsMemorySize(0);
if (this == rootView.texture)
--textureCount;
if (rootView.texture != this && rootView.texture != nullptr)
rootView.texture->release();
if (parentView.texture != this && parentView.texture != nullptr)
parentView.texture->release();
}
void Texture::setGraphicsMemorySize(int64 bytes)
@@ -352,18 +470,18 @@ void Texture::draw(Graphics *gfx, const Matrix4 &m)
void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
{
if (!readable)
throw love::Exception("Textures with non-readable formats cannot be drawn.");
if (renderTarget && gfx->isRenderTargetActive(this))
throw love::Exception("Cannot render a Texture to itself.");
if (texType == TEXTURE_2D_ARRAY)
{
drawLayer(gfx, q->getLayer(), q, localTransform);
return;
}
if (!readable)
throw love::Exception("Textures with non-readable formats cannot be drawn.");
if (renderTarget && gfx->isRenderTargetActive(this))
throw love::Exception("Cannot render a Texture to itself.");
const Matrix4 &tm = gfx->getTransform();
bool is2D = tm.isAffine2DTransform();
@@ -582,36 +700,6 @@ void Texture::generateMipmaps()
generateMipmapsInternal();
}
TextureType Texture::getTextureType() const
{
return texType;
}
PixelFormat Texture::getPixelFormat() const
{
return format;
}
Texture::MipmapsMode Texture::getMipmapsMode() const
{
return mipmapsMode;
}
bool Texture::isRenderTarget() const
{
return renderTarget;
}
bool Texture::isComputeWritable() const
{
return computeWrite;
}
bool Texture::isReadable() const
{
return readable;
}
bool Texture::isCompressed() const
{
return isPixelFormatCompressed(format);
@@ -685,28 +773,39 @@ int Texture::getRequestedMSAA() const
return requestedMSAA;
}
void Texture::setSamplerState(const SamplerState &s)
const SamplerState &Texture::getSamplerState() const
{
return samplerState;
}
SamplerState Texture::validateSamplerState(SamplerState s) const
{
if (!readable)
return;
return s;
if (s.depthSampleMode.hasValue && !isPixelFormatDepth(format))
throw love::Exception("Only depth textures can have a depth sample compare mode.");
Graphics::flushBatchedDrawsGlobal();
samplerState = s;
if (samplerState.mipmapFilter != SamplerState::MIPMAP_FILTER_NONE && getMipmapCount() == 1)
samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
if (s.mipmapFilter != SamplerState::MIPMAP_FILTER_NONE && getMipmapCount() == 1)
s.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
if (texType == TEXTURE_CUBE)
samplerState.wrapU = samplerState.wrapV = samplerState.wrapW = SamplerState::WRAP_CLAMP;
}
s.wrapU = s.wrapV = s.wrapW = SamplerState::WRAP_CLAMP;
const SamplerState &Texture::getSamplerState() const
{
return samplerState;
if (s.minFilter == SamplerState::FILTER_LINEAR || s.magFilter == SamplerState::FILTER_LINEAR || s.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR)
{
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
if (!gfx->isPixelFormatSupported(format, PIXELFORMATUSAGEFLAGS_LINEAR))
{
s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST;
if (s.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR)
s.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST;
}
}
Graphics::flushBatchedDrawsGlobal();
return s;
}
Quad *Texture::getQuad() const
@@ -781,6 +880,35 @@ bool Texture::validateDimensions(bool throwException) const
return success;
}
void Texture::validatePixelFormat(Graphics *gfx) const
{
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))
{
const char *fstr = "unknown";
love::getConstant(format, fstr);
const char *readablestr = "";
if (readable != !isPixelFormatDepthStencil(format))
readablestr = readable ? " readable" : " non-readable";
const char *rtstr = "";
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);
}
}
Texture::Slices::Slices(TextureType textype)
: textureType(textype)
{
@@ -971,6 +1099,7 @@ static StringMap<Texture::SettingType, Texture::SETTING_MAX_ENUM>::Entry setting
{ "msaa", Texture::SETTING_MSAA },
{ "canvas", Texture::SETTING_RENDER_TARGET },
{ "computewrite", Texture::SETTING_COMPUTE_WRITE },
{ "viewformats", Texture::SETTING_VIEW_FORMATS },
{ "readable", Texture::SETTING_READABLE },
{ "debugname", Texture::SETTING_DEBUGNAME },
};
+42 -10
View File
@@ -174,6 +174,7 @@ public:
SETTING_MSAA,
SETTING_RENDER_TARGET,
SETTING_COMPUTE_WRITE,
SETTING_VIEW_FORMATS,
SETTING_READABLE,
SETTING_DEBUGNAME,
SETTING_MAX_ENUM
@@ -194,10 +195,22 @@ public:
int msaa = 1;
bool renderTarget = false;
bool computeWrite = false;
bool viewFormats = false;
OptionalBool readable;
std::string debugName;
};
struct ViewSettings
{
Optional<PixelFormat> format;
Optional<TextureType> type;
OptionalInt mipmapStart;
OptionalInt mipmapCount;
OptionalInt layerStart;
OptionalInt layerCount;
std::string debugName;
};
struct Slices
{
public:
@@ -228,10 +241,14 @@ public:
}; // Slices
static int64 totalGraphicsMemory;
struct ViewInfo
{
Texture *texture;
int startMipmap;
int startLayer;
};
Texture(Graphics *gfx, const Settings &settings, const Slices *slices);
virtual ~Texture();
static int64 totalGraphicsMemory;
// Drawable.
void draw(Graphics *gfx, const Matrix4 &m) override;
@@ -255,13 +272,14 @@ public:
virtual ptrdiff_t getRenderTargetHandle() const = 0;
virtual ptrdiff_t getSamplerHandle() const = 0;
TextureType getTextureType() const;
PixelFormat getPixelFormat() const;
MipmapsMode getMipmapsMode() const;
TextureType getTextureType() const { return texType; }
PixelFormat getPixelFormat() const { return format; }
MipmapsMode getMipmapsMode() const { return mipmapsMode; }
bool isRenderTarget() const;
bool isComputeWritable() const;
bool isReadable() const;
bool isRenderTarget() const { return renderTarget; }
bool isComputeWritable() const { return computeWrite; }
bool isReadable() const { return readable; }
bool hasViewFormats() const { return viewFormats; }
bool isCompressed() const;
bool isFormatLinear() const;
@@ -285,11 +303,14 @@ public:
int getRequestedMSAA() const;
virtual int getMSAA() const = 0;
virtual void setSamplerState(const SamplerState &s);
virtual void setSamplerState(const SamplerState &s) = 0;
const SamplerState &getSamplerState() const;
Quad *getQuad() const;
const ViewInfo &getRootViewInfo() const { return rootView; }
const ViewInfo &getParentViewInfo() const { return parentView; }
const std::string &getDebugName() const { return debugName; }
static int getTotalMipmapCount(int w, int h);
@@ -310,6 +331,10 @@ public:
protected:
Texture(Graphics *gfx, const Settings &settings, const Slices *slices);
Texture(Graphics *gfx, Texture *base, const ViewSettings &viewsettings);
virtual ~Texture();
void setGraphicsMemorySize(int64 size);
void uploadImageData(love::image::ImageDataBase *d, int level, int slice, int x, int y);
@@ -318,13 +343,17 @@ protected:
bool supportsGenerateMipmaps(const char *&outReason) const;
virtual void generateMipmapsInternal() = 0;
SamplerState validateSamplerState(SamplerState s) const;
bool validateDimensions(bool throwException) const;
void validatePixelFormat(Graphics *gfx) const;
TextureType texType;
PixelFormat format;
bool renderTarget;
bool computeWrite;
bool viewFormats;
bool readable;
MipmapsMode mipmapsMode;
@@ -349,6 +378,9 @@ protected:
std::string debugName;
ViewInfo rootView;
ViewInfo parentView;
}; // Texture
} // graphics
+1
View File
@@ -61,6 +61,7 @@ public:
virtual ~Graphics();
love::graphics::Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) override;
love::graphics::Texture *newTextureView(love::graphics::Texture *base, const Texture::ViewSettings &viewsettings) override;
love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
Matrix4 computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const override;
+5
View File
@@ -433,6 +433,11 @@ love::graphics::Texture *Graphics::newTexture(const Texture::Settings &settings,
return new Texture(this, device, settings, data);
}
love::graphics::Texture *Graphics::newTextureView(love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
{
return new Texture(this, device, base, viewsettings);
}
love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles)
{
return new ShaderStage(this, stage, source, gles, cachekey);
+5 -4
View File
@@ -38,6 +38,7 @@ class Texture final : public love::graphics::Texture
public:
Texture(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settings &settings, const Slices *data);
Texture(love::graphics::Graphics *gfx, id<MTLDevice> device, love::graphics::Texture *base, const Texture::ViewSettings &viewsettings);
virtual ~Texture();
void copyFromBuffer(love::graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect) override;
@@ -58,11 +59,11 @@ private:
void uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r) override;
void generateMipmapsInternal() override;
id<MTLTexture> texture;
id<MTLTexture> msaaTexture;
id<MTLSamplerState> sampler;
id<MTLTexture> texture = nil;
id<MTLTexture> msaaTexture = nil;
id<MTLSamplerState> sampler = nil;
int actualMSAASamples;
int actualMSAASamples = 1;
}; // Texture
+39 -8
View File
@@ -43,10 +43,6 @@ static MTLTextureType getMTLTextureType(TextureType type, int msaa)
Texture::Texture(love::graphics::Graphics *gfxbase, id<MTLDevice> device, const Settings &settings, const Slices *data)
: love::graphics::Texture(gfxbase, settings, data)
, texture(nil)
, msaaTexture(nil)
, sampler(nil)
, actualMSAASamples(1)
{ @autoreleasepool {
auto gfx = (Graphics *) gfxbase;
@@ -80,6 +76,8 @@ Texture::Texture(love::graphics::Graphics *gfxbase, id<MTLDevice> device, const
desc.usage |= MTLTextureUsageRenderTarget;
if (computeWrite)
desc.usage |= MTLTextureUsageShaderWrite;
if (viewFormats)
desc.usage |= MTLTextureUsagePixelFormatView;
texture = [device newTextureWithDescriptor:desc];
@@ -212,6 +210,41 @@ Texture::Texture(love::graphics::Graphics *gfxbase, id<MTLDevice> device, const
setSamplerState(samplerState);
}}
Texture::Texture(love::graphics::Graphics *gfx, id<MTLDevice> device, love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
: love::graphics::Texture(gfx, base, viewsettings)
{
id<MTLTexture> basetex = ((Texture *) base)->texture;
auto formatdesc = Metal::convertPixelFormat(device, format);
int slices = texType == TEXTURE_CUBE ? 6 : getLayerCount();
if (formatdesc.swizzled)
{
if (@available(macOS 10.15, iOS 13, *))
{
texture = [basetex newTextureViewWithPixelFormat:formatdesc.format
textureType:getMTLTextureType(texType, 1)
levels:NSMakeRange(parentView.startMipmap, mipmapCount)
slices:NSMakeRange(parentView.startLayer, slices)
swizzle:formatdesc.swizzle];
}
}
else
{
texture = [basetex newTextureViewWithPixelFormat:formatdesc.format
textureType:getMTLTextureType(texType, 1)
levels:NSMakeRange(parentView.startMipmap, mipmapCount)
slices:NSMakeRange(parentView.startLayer, slices)];
}
if (texture == nil)
throw love::Exception("Could not create Metal texture view.");
if (!debugName.empty())
texture.label = @(debugName.c_str());
setSamplerState(samplerState);
}
Texture::~Texture()
{ @autoreleasepool {
texture = nil;
@@ -340,10 +373,8 @@ void Texture::setSamplerState(const SamplerState &s)
if (s.depthSampleMode.hasValue && !Graphics::getInstance()->isDepthCompareSamplerSupported())
throw love::Exception("Depth comparison sampling in shaders is not supported on this system.");
// Base class does common validation and assigns samplerState.
love::graphics::Texture::setSamplerState(s);
sampler = Graphics::getInstance()->getCachedSampler(s);
samplerState = validateSamplerState(s);
sampler = Graphics::getInstance()->getCachedSampler(samplerState);
}}
} // metal
+5
View File
@@ -158,6 +158,11 @@ love::graphics::Texture *Graphics::newTexture(const Texture::Settings &settings,
return new Texture(this, settings, data);
}
love::graphics::Texture *Graphics::newTextureView(love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
{
return new Texture(this, base, viewsettings);
}
love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles)
{
return new ShaderStage(this, stage, source, gles, cachekey);
+1
View File
@@ -57,6 +57,7 @@ public:
virtual ~Graphics();
love::graphics::Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) override;
love::graphics::Texture *newTextureView(love::graphics::Texture *base, const Texture::ViewSettings &viewsettings) override;
love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
Matrix4 computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const override;
+2
View File
@@ -2506,6 +2506,8 @@ const char *OpenGL::debugSeverityString(GLenum severity)
return "medium";
case GL_DEBUG_SEVERITY_LOW:
return "low";
case GL_DEBUG_SEVERITY_NOTIFICATION:
return "notification";
default:
return "unknown";
}
+43 -14
View File
@@ -244,6 +244,25 @@ Texture::Texture(love::graphics::Graphics *gfx, const Settings &settings, const
slices.clear();
}
Texture::Texture(love::graphics::Graphics *gfx, love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
: love::graphics::Texture(gfx, base, viewsettings)
, slices(viewsettings.type.get(base->getTextureType()))
, fbo(0)
, texture(0)
, renderbuffer(0)
, framebufferStatus(GL_FRAMEBUFFER_COMPLETE)
, textureGLError(GL_NO_ERROR)
, actualSamples(1)
{
if (!loadVolatile())
{
if (framebufferStatus != GL_FRAMEBUFFER_COMPLETE)
throw love::Exception("Cannot create texture view (OpenGL framebuffer error: %s)", OpenGL::framebufferStatusString(framebufferStatus));
if (textureGLError != GL_NO_ERROR)
throw love::Exception("Cannot create texture view (OpenGL error: %s)", OpenGL::errorString(textureGLError));
}
}
Texture::~Texture()
{
unloadVolatile();
@@ -254,11 +273,26 @@ void Texture::createTexture()
// The base class handles some validation. For example, if ImageData is
// given then it must exist for all mip levels, a render target can't use
// a compressed format, etc.
glGenTextures(1, &texture);
GLenum gltype = OpenGL::getGLTextureType(texType);
if (parentView.texture != this)
{
OpenGL::TextureFormat fmt = gl.convertPixelFormat(format, false);
Texture *basetex = (Texture *) parentView.texture;
int layers = texType == TEXTURE_CUBE ? 6 : getLayerCount();
glTextureView(texture, gltype, basetex->texture, fmt.internalformat,
parentView.startMipmap, getMipmapCount(),
parentView.startLayer, layers);
gl.bindTextureToUnit(this, 0, false);
setSamplerState(samplerState);
return;
}
gl.bindTextureToUnit(this, 0, false);
GLenum gltype = OpenGL::getGLTextureType(texType);
if (renderTarget && GLAD_ANGLE_texture_usage)
glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
@@ -371,6 +405,12 @@ bool Texture::loadVolatile()
if (texture != 0 || renderbuffer != 0)
return true;
if (parentView.texture != this)
{
Texture *basetex = (Texture *) parentView.texture;
basetex->loadVolatile();
}
OpenGL::TempDebugGroup debuggroup("Texture load");
// NPOT textures don't support mipmapping without full NPOT support.
@@ -593,18 +633,7 @@ void Texture::setSamplerState(const SamplerState &s)
if (s.depthSampleMode.hasValue && !gl.isDepthCompareSampleSupported())
throw love::Exception("Depth comparison sampling in shaders is not supported on this system.");
// Base class does common validation and assigns samplerState.
love::graphics::Texture::setSamplerState(s);
auto supportedflags = OpenGL::getPixelFormatUsageFlags(getPixelFormat());
if ((supportedflags & PIXELFORMATUSAGEFLAGS_LINEAR) == 0)
{
samplerState.magFilter = samplerState.minFilter = SamplerState::FILTER_NEAREST;
if (samplerState.mipmapFilter == SamplerState::MIPMAP_FILTER_LINEAR)
samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NEAREST;
}
samplerState = validateSamplerState(s);
// If we only have limited NPOT support then the wrap mode must be CLAMP.
if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
+2
View File
@@ -39,6 +39,7 @@ class Texture final : public love::graphics::Texture, public Volatile
public:
Texture(love::graphics::Graphics *gfx, const Settings &settings, const Slices *data);
Texture(love::graphics::Graphics *gfx, love::graphics::Texture *base, const Texture::ViewSettings &viewsettings);
virtual ~Texture();
@@ -61,6 +62,7 @@ public:
void readbackInternal(int slice, int mipmap, const Rect &rect, int destwidth, size_t size, void *dest);
private:
void createTexture();
void uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r) override;
+5
View File
@@ -168,6 +168,11 @@ love::graphics::Texture *Graphics::newTexture(const love::graphics::Texture::Set
return new Texture(this, settings, data);
}
love::graphics::Texture *Graphics::newTextureView(love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
{
return new Texture(this, base, viewsettings);
}
love::graphics::Buffer *Graphics::newBuffer(const love::graphics::Buffer::Settings &settings, const std::vector<love::graphics::Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
{
return new Buffer(this, settings, format, data, size, arraylength);
+1
View File
@@ -275,6 +275,7 @@ public:
// implementation for virtual functions
love::graphics::Texture *newTexture(const love::graphics::Texture::Settings &settings, const love::graphics::Texture::Slices *data) override;
love::graphics::Texture *newTextureView(love::graphics::Texture *base, const Texture::ViewSettings &viewsettings) override;
love::graphics::Buffer *newBuffer(const love::graphics::Buffer::Settings &settings, const std::vector<love::graphics::Buffer::DataDeclaration>& format, const void *data, size_t size, size_t arraylength) override;
graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset) override;
graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty) override;
+129 -93
View File
@@ -47,11 +47,22 @@ Texture::Texture(love::graphics::Graphics *gfx, const Settings &settings, const
slices.clear();
}
Texture::Texture(love::graphics::Graphics *gfx, love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
: love::graphics::Texture(gfx, base, viewsettings)
, vgfx(dynamic_cast<Graphics*>(gfx))
, slices(viewsettings.type.get(base->getTextureType()))
, imageAspect(0)
{
loadVolatile();
}
bool Texture::loadVolatile()
{
allocator = vgfx->getVmaAllocator();
device = vgfx->getDevice();
bool root = rootView.texture == this;
if (isPixelFormatDepth(format))
imageAspect |= VK_IMAGE_ASPECT_DEPTH_BIT;
if (isPixelFormatStencil(format))
@@ -79,82 +90,96 @@ bool Texture::loadVolatile()
usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
VkImageCreateFlags createFlags = 0;
layerCount = 1;
if (texType == TEXTURE_2D_ARRAY)
layerCount = getLayerCount();
else if (texType == TEXTURE_CUBE)
{
layerCount = 6;
createFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
}
msaaSamples = vgfx->getMsaaCount(requestedMSAA);
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.flags = createFlags;
imageInfo.imageType = Vulkan::getImageType(getTextureType());
imageInfo.extent.width = static_cast<uint32_t>(pixelWidth);
imageInfo.extent.height = static_cast<uint32_t>(pixelHeight);
imageInfo.extent.depth = static_cast<uint32_t>(depth);
imageInfo.arrayLayers = static_cast<uint32_t>(layerCount);
imageInfo.mipLevels = static_cast<uint32_t>(mipmapCount);
imageInfo.format = vulkanFormat.internalFormat;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usageFlags;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = msaaSamples;
VmaAllocationCreateInfo imageAllocationCreateInfo{};
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS)
throw love::Exception("failed to create image");
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
if (isPixelFormatDepthStencil(format))
imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
else if (computeWrite)
imageLayout = VK_IMAGE_LAYOUT_GENERAL;
else
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_UNDEFINED, imageLayout,
0, VK_REMAINING_MIP_LEVELS,
0, VK_REMAINING_ARRAY_LAYERS);
bool hasdata = slices.get(0, 0) != nullptr;
if (hasdata)
if (root)
{
for (int mip = 0; mip < getMipmapCount(); mip++)
{
int sliceCount;
if (texType == TEXTURE_CUBE)
sliceCount = 6;
else
sliceCount = slices.getSliceCount();
VkImageCreateFlags createFlags = 0;
for (int slice = 0; slice < sliceCount; slice++)
if (viewFormats)
createFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
if (texType == TEXTURE_CUBE || texType == TEXTURE_2D_ARRAY)
createFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
msaaSamples = vgfx->getMsaaCount(requestedMSAA);
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.flags = createFlags;
imageInfo.imageType = Vulkan::getImageType(getTextureType());
imageInfo.extent.width = static_cast<uint32_t>(pixelWidth);
imageInfo.extent.height = static_cast<uint32_t>(pixelHeight);
imageInfo.extent.depth = static_cast<uint32_t>(depth);
imageInfo.arrayLayers = static_cast<uint32_t>(layerCount);
imageInfo.mipLevels = static_cast<uint32_t>(mipmapCount);
imageInfo.format = vulkanFormat.internalFormat;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usageFlags;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = msaaSamples;
VmaAllocationCreateInfo imageAllocationCreateInfo{};
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS)
throw love::Exception("failed to create image");
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
if (isPixelFormatDepthStencil(format))
imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
else if (computeWrite)
imageLayout = VK_IMAGE_LAYOUT_GENERAL;
else
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
VK_IMAGE_LAYOUT_UNDEFINED, imageLayout,
0, VK_REMAINING_MIP_LEVELS,
0, VK_REMAINING_ARRAY_LAYERS);
bool hasdata = slices.get(0, 0) != nullptr;
if (hasdata)
{
for (int mip = 0; mip < getMipmapCount(); mip++)
{
auto id = slices.get(slice, mip);
if (id != nullptr)
uploadImageData(id, mip, slice, 0, 0);
int sliceCount;
if (texType == TEXTURE_CUBE)
sliceCount = 6;
else
sliceCount = slices.getSliceCount();
for (int slice = 0; slice < sliceCount; slice++)
{
auto id = slices.get(slice, mip);
if (id != nullptr)
uploadImageData(id, mip, slice, 0, 0);
}
}
}
else
clear();
}
else
clear();
{
Texture *roottex = (Texture *) rootView.texture;
textureImage = roottex->textureImage;
textureImageAllocation = VK_NULL_HANDLE;
imageLayout = roottex->imageLayout;
msaaSamples = roottex->msaaSamples;
}
createTextureImageView();
textureSampler = vgfx->getCachedSampler(samplerState);
setSamplerState(samplerState);
if (!isPixelFormatDepthStencil(format) && slices.getMipmapCount() <= 1 && getMipmapsMode() != MIPMAPS_NONE)
if (root && !isPixelFormatDepthStencil(format) && slices.getMipmapCount() <= 1 && getMipmapsMode() != MIPMAPS_NONE)
generateMipmaps();
if (renderTarget)
@@ -172,9 +197,9 @@ bool Texture::loadVolatile()
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
viewInfo.format = vulkanFormat.internalFormat;
viewInfo.subresourceRange.aspectMask = imageAspect;
viewInfo.subresourceRange.baseMipLevel = mip;
viewInfo.subresourceRange.baseMipLevel = mip + rootView.startMipmap;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = slice;
viewInfo.subresourceRange.baseArrayLayer = slice + rootView.startLayer;
viewInfo.subresourceRange.layerCount = 1;
viewInfo.components.r = vulkanFormat.swizzleR;
viewInfo.components.g = vulkanFormat.swizzleG;
@@ -189,15 +214,18 @@ bool Texture::loadVolatile()
int64 memsize = 0;
for (int mip = 0; mip < getMipmapCount(); mip++)
if (root)
{
int w = getPixelWidth(mip);
int h = getPixelHeight(mip);
int slices = getDepth(mip) * layerCount;
memsize += getPixelFormatSliceSize(format, w, h) * slices;
}
for (int mip = 0; mip < getMipmapCount(); mip++)
{
int w = getPixelWidth(mip);
int h = getPixelHeight(mip);
int slices = getDepth(mip) * layerCount;
memsize += getPixelFormatSliceSize(format, w, h) * slices;
}
memsize *= static_cast<int>(msaaSamples);
memsize *= static_cast<int>(msaaSamples);
}
setGraphicsMemorySize(memsize);
@@ -207,8 +235,16 @@ bool Texture::loadVolatile()
{
VkDebugUtilsObjectNameInfoEXT nameInfo{};
nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;
nameInfo.objectHandle = (uint64_t)textureImage;
if (root)
{
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;
nameInfo.objectHandle = (uint64_t)textureImage;
}
else
{
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE_VIEW;
nameInfo.objectHandle = (uint64_t)textureImageView;
}
nameInfo.pObjectName = debugName.c_str();
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
}
@@ -230,13 +266,15 @@ void Texture::unloadVolatile()
textureImageAllocation = textureImageAllocation,
textureImageViews = std::move(renderTargetImageViews)] () {
vkDestroyImageView(device, textureImageView, nullptr);
vmaDestroyImage(allocator, textureImage, textureImageAllocation);
if (textureImageAllocation)
vmaDestroyImage(allocator, textureImage, textureImageAllocation);
for (const auto &views : textureImageViews)
for (const auto &view : views)
vkDestroyImageView(device, view, nullptr);
});
textureImage = VK_NULL_HANDLE;
textureImageAllocation = VK_NULL_HANDLE;
setGraphicsMemorySize(0);
}
@@ -278,8 +316,7 @@ ptrdiff_t Texture::getHandle() const
void Texture::setSamplerState(const SamplerState &s)
{
love::graphics::Texture::setSamplerState(s);
samplerState = validateSamplerState(s);
textureSampler = vgfx->getCachedSampler(samplerState);
}
@@ -291,16 +328,15 @@ VkImageLayout Texture::getImageLayout() const
void Texture::createTextureImageView()
{
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = textureImage;
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
viewInfo.format = vulkanFormat.internalFormat;
viewInfo.subresourceRange.aspectMask = imageAspect;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.baseMipLevel = rootView.startMipmap;
viewInfo.subresourceRange.levelCount = getMipmapCount();
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.baseArrayLayer = rootView.startLayer;
viewInfo.subresourceRange.layerCount = layerCount;
viewInfo.components.r = vulkanFormat.swizzleR;
viewInfo.components.g = vulkanFormat.swizzleG;
@@ -404,16 +440,16 @@ void Texture::generateMipmapsInternal()
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.baseArrayLayer = rootView.startLayer;
barrier.subresourceRange.layerCount = static_cast<uint32_t>(layerCount);
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.baseMipLevel = rootView.startMipmap;
barrier.subresourceRange.levelCount = 1u;
uint32_t mipLevels = static_cast<uint32_t>(getMipmapCount());
for (uint32_t i = 1; i < mipLevels; i++)
{
barrier.subresourceRange.baseMipLevel = i - 1;
barrier.subresourceRange.baseMipLevel = rootView.startMipmap + i - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
@@ -429,15 +465,15 @@ void Texture::generateMipmapsInternal()
blit.srcOffsets[0] = { 0, 0, 0 };
blit.srcOffsets[1] = { getPixelWidth(i - 1), getPixelHeight(i - 1), 1 };
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = i - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.mipLevel = rootView.startMipmap + i - 1;
blit.srcSubresource.baseArrayLayer = rootView.startLayer;
blit.srcSubresource.layerCount = static_cast<uint32_t>(layerCount);
blit.dstOffsets[0] = { 0, 0, 0 };
blit.dstOffsets[1] = { getPixelWidth(i), getPixelHeight(i), 1 };
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = i;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.mipLevel = rootView.startMipmap + i;
blit.dstSubresource.baseArrayLayer = rootView.startLayer;
blit.dstSubresource.layerCount = static_cast<uint32_t>(layerCount);
vkCmdBlitImage(commandBuffer,
@@ -458,7 +494,7 @@ void Texture::generateMipmapsInternal()
1, &barrier);
}
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
barrier.subresourceRange.baseMipLevel = rootView.startMipmap + mipLevels - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
@@ -496,11 +532,11 @@ void Texture::uploadByteData(const void *data, size_t size, int level, int slice
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
uint32_t baseLayer;
if (getTextureType() == TEXTURE_VOLUME)
baseLayer = 0;
else
baseLayer = slice;
uint32_t baseLayer = rootView.startLayer;
if (getTextureType() != TEXTURE_VOLUME)
baseLayer += slice;
level += rootView.startMipmap;
region.imageSubresource.aspectMask = imageAspect;
region.imageSubresource.mipLevel = level;
@@ -558,8 +594,8 @@ void Texture::copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int
VkImageSubresourceLayers layers{};
layers.aspectMask = imageAspect;
layers.mipLevel = mipmap;
layers.baseArrayLayer = slice;
layers.mipLevel = mipmap + rootView.startMipmap;
layers.baseArrayLayer = slice + rootView.startLayer;
layers.layerCount = 1;
VkBufferImageCopy region{};
@@ -588,8 +624,8 @@ void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const
VkImageSubresourceLayers layers{};
layers.aspectMask = imageAspect;
layers.mipLevel = mipmap;
layers.baseArrayLayer = slice;
layers.mipLevel = mipmap + rootView.startMipmap;
layers.baseArrayLayer = slice + rootView.startLayer;
layers.layerCount = 1;
VkBufferImageCopy region{};
+7 -5
View File
@@ -40,11 +40,13 @@ class Texture final
, public Volatile
{
public:
Texture(love::graphics::Graphics *gfx, const Settings &settings, const Slices *data);
~Texture();
virtual bool loadVolatile() override;
virtual void unloadVolatile() override;
Texture(love::graphics::Graphics *gfx, const Settings &settings, const Slices *data);
Texture(love::graphics::Graphics *gfx, love::graphics::Texture *base, const Texture::ViewSettings &viewsettings);
virtual ~Texture();
bool loadVolatile() override;
void unloadVolatile() override;
void setSamplerState(const SamplerState &s) override;
@@ -61,7 +63,7 @@ public:
void uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r) override;
void generateMipmapsInternal() override;
void generateMipmapsInternal() override;
int getMSAA() const override;
ptrdiff_t getHandle() const override;
+69
View File
@@ -799,6 +799,13 @@ static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool chec
if (s.type == TEXTURE_2D_ARRAY || s.type == TEXTURE_VOLUME)
s.layers = luax_checkintflag(L, idx, Texture::getConstant(Texture::SETTING_LAYERS));
}
else
{
s.width = luax_intflag(L, idx, Texture::getConstant(Texture::SETTING_WIDTH), s.width);
s.height = luax_intflag(L, idx, Texture::getConstant(Texture::SETTING_HEIGHT), s.height);
if (s.type == TEXTURE_2D_ARRAY || s.type == TEXTURE_VOLUME)
s.layers = luax_intflag(L, idx, Texture::getConstant(Texture::SETTING_LAYERS), s.layers);
}
lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_MIPMAPS));
if (!lua_isnoneornil(L, -1))
@@ -823,6 +830,7 @@ static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool chec
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);
s.viewFormats = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_VIEW_FORMATS), s.viewFormats);
lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_READABLE));
if (!lua_isnoneornil(L, -1))
@@ -1240,6 +1248,66 @@ int w_newVolumeImage(lua_State *L)
return w_newVolumeTexture(L);
}
int w_newTextureView(lua_State *L)
{
Texture *base = luax_checktexture(L, 1);
luaL_checktype(L, 2, LUA_TTABLE);
Texture::ViewSettings settings;
lua_getfield(L, 2, "format");
if (!lua_isnoneornil(L, -1))
{
const char *str = luaL_checkstring(L, -1);
if (!getConstant(str, settings.format.value))
luax_enumerror(L, "pixel format", str);
settings.format.hasValue = true;
}
lua_pop(L, 1);
lua_getfield(L, 2, "type");
if (!lua_isnoneornil(L, -1))
{
const char *str = luaL_checkstring(L, -1);
if (!Texture::getConstant(str, settings.type.value))
luax_enumerror(L, "texture type", Texture::getConstants(settings.type.value), str);
settings.type.hasValue = true;
}
lua_pop(L, 1);
lua_getfield(L, 2, "mipmapstart");
if (!lua_isnoneornil(L, -1))
settings.mipmapStart.set(luaL_checkint(L, -1) - 1);
lua_pop(L, 1);
lua_getfield(L, 2, "mipmapcount");
if (!lua_isnoneornil(L, -1))
settings.mipmapCount.set(luaL_checkint(L, -1));
lua_pop(L, 1);
lua_getfield(L, 2, "layerstart");
if (!lua_isnoneornil(L, -1))
settings.layerStart.set(luaL_checkint(L, -1) - 1);
lua_pop(L, 1);
lua_getfield(L, 2, "layers");
if (!lua_isnoneornil(L, -1))
settings.layerCount.set(luaL_checkint(L, -1));
lua_pop(L, 1);
lua_getfield(L, 2, "debugname");
if (!lua_isnoneornil(L, -1))
settings.debugName = luaL_checkstring(L, -1);
lua_pop(L, 1);
Texture *t = nullptr;
luax_catchexcept(L, [&]() { t = instance()->newTextureView(base, settings); });
luax_pushtype(L, t);
t->release();
return 1;
}
int w_newQuad(lua_State *L)
{
luax_checkgraphicscreated(L);
@@ -3857,6 +3925,7 @@ static const luaL_Reg functions[] =
{ "newCubeTexture", w_newCubeTexture },
{ "newArrayTexture", w_newArrayTexture },
{ "newVolumeTexture", w_newVolumeTexture },
{ "newTextureView", w_newTextureView },
{ "newQuad", w_newQuad },
{ "newFont", w_newFont },
{ "newImageFont", w_newImageFont },
+8
View File
@@ -300,6 +300,13 @@ int w_Texture_isReadable(lua_State *L)
return 1;
}
int w_Texture_hasViewFormats(lua_State *L)
{
Texture *t = luax_checktexture(L, 1);
luax_pushboolean(L, t->hasViewFormats());
return 1;
}
int w_Texture_setDepthSampleMode(lua_State *L)
{
Texture *t = luax_checktexture(L, 1);
@@ -511,6 +518,7 @@ const luaL_Reg w_Texture_functions[] =
{ "isCanvas", w_Texture_isCanvas },
{ "isComputeWritable", w_Texture_isComputeWritable },
{ "isReadable", w_Texture_isReadable },
{ "hasViewFormats", w_Texture_hasViewFormats },
{ "getMipmapMode", w_Texture_getMipmapMode },
{ "getDepthSampleMode", w_Texture_getDepthSampleMode },
{ "setDepthSampleMode", w_Texture_setDepthSampleMode },