mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
Merge branch '12.0-development' into metal
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "Graphics.h"
|
||||
#include "common/memory.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -48,15 +49,20 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
|
||||
bool indexbuffer = settings.typeFlags & TYPEFLAG_INDEX;
|
||||
bool vertexbuffer = settings.typeFlags & TYPEFLAG_VERTEX;
|
||||
bool texelbuffer = settings.typeFlags & TYPEFLAG_TEXEL;
|
||||
bool storagebuffer = settings.typeFlags & TYPEFLAG_SHADER_STORAGE;
|
||||
|
||||
if (!indexbuffer && !vertexbuffer && !texelbuffer)
|
||||
throw love::Exception("Buffer must be created with at least one buffer type (index, vertex, or texel).");
|
||||
if (!indexbuffer && !vertexbuffer && !texelbuffer && !storagebuffer)
|
||||
throw love::Exception("Buffer must be created with at least one buffer type (index, vertex, texel, or shaderstorage).");
|
||||
|
||||
if (texelbuffer && !caps.features[Graphics::FEATURE_TEXEL_BUFFER])
|
||||
throw love::Exception("Texel buffers are not supported on this system.");
|
||||
|
||||
if (storagebuffer && !caps.features[Graphics::FEATURE_GLSL4])
|
||||
throw love::Exception("Shader Storage buffers are not supported on this system (GLSL 4 support is necessary.)");
|
||||
|
||||
size_t offset = 0;
|
||||
size_t stride = 0;
|
||||
size_t structurealignment = 1;
|
||||
|
||||
for (const DataDeclaration &decl : bufferformat)
|
||||
{
|
||||
@@ -116,16 +122,60 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
|
||||
throw love::Exception("Signed normalized formats are not supported in texel buffers.");
|
||||
}
|
||||
|
||||
// TODO: alignment
|
||||
member.offset = offset;
|
||||
member.size = member.info.size;
|
||||
size_t memberoffset = offset;
|
||||
size_t membersize = member.info.size;
|
||||
|
||||
offset += member.size;
|
||||
// Storage buffers are always treated as being an array of a structure.
|
||||
// The structure's contents are the buffer format declaration.
|
||||
if (storagebuffer)
|
||||
{
|
||||
// TODO: We can support these.
|
||||
if (decl.arrayLength > 0)
|
||||
throw love::Exception("Arrays are not currently supported in shader storage buffers.");
|
||||
|
||||
if (info.baseType == DATA_BASETYPE_BOOL)
|
||||
throw love::Exception("Bool types are not supported in shader storage buffers.");
|
||||
|
||||
if (info.baseType == DATA_BASETYPE_UNORM || info.baseType == DATA_BASETYPE_SNORM)
|
||||
throw love::Exception("Normalized formats are not supported in shader storage buffers.");
|
||||
|
||||
size_t alignment = 1;
|
||||
|
||||
// GLSL's std430 packing rules. We also assume all matrices are
|
||||
// column-major.
|
||||
if (info.isMatrix)
|
||||
alignment = info.matrixRows * info.componentSize;
|
||||
else
|
||||
alignment = info.components * info.componentSize;
|
||||
|
||||
structurealignment = std::max(structurealignment, alignment);
|
||||
|
||||
memberoffset = alignUp(memberoffset, alignment);
|
||||
|
||||
if (memberoffset != offset && (indexbuffer || vertexbuffer || texelbuffer))
|
||||
throw love::Exception("Cannot create Buffer:\nInternal alignment of member '%s' is preventing Buffer from being created as both a shader storage buffer and other buffer types\nMember byte offset needed for shader storage buffer: %d\nMember byte offset needed for other buffer types: %d",
|
||||
member.decl.name.c_str(), memberoffset, offset);
|
||||
}
|
||||
|
||||
member.offset = memberoffset;
|
||||
member.size = membersize;
|
||||
|
||||
offset = member.offset + member.size;
|
||||
|
||||
dataMembers.push_back(member);
|
||||
}
|
||||
|
||||
stride = offset;
|
||||
stride = alignUp(offset, structurealignment);
|
||||
|
||||
if (storagebuffer && (indexbuffer || vertexbuffer || texelbuffer))
|
||||
{
|
||||
if (stride != offset)
|
||||
throw love::Exception("Cannot create Buffer:\nBuffer used as a shader storage buffer would have a different number of bytes per array element (%d) than when used as other buffer types (%d)",
|
||||
stride, offset);
|
||||
}
|
||||
|
||||
if (storagebuffer && stride > SHADER_STORAGE_BUFFER_MAX_STRIDE)
|
||||
throw love::Exception("Shader storage buffers cannot have more than %d bytes within each array element.", SHADER_STORAGE_BUFFER_MAX_STRIDE);
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
@@ -144,7 +194,8 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDe
|
||||
this->size = size;
|
||||
|
||||
if (texelbuffer && arraylength * dataMembers.size() > caps.limits[Graphics::LIMIT_TEXEL_BUFFER_SIZE])
|
||||
throw love::Exception("Cannot create texel buffer: total number of values in the buffer (%d * %d) is too large for this system (maximum %d).", (int) dataMembers.size(), (int) arraylength, caps.limits[Graphics::LIMIT_TEXEL_BUFFER_SIZE]);
|
||||
throw love::Exception("Cannot create texel buffer: total number of values in the buffer (%d * %d) is too large for this system (maximum %d).",
|
||||
(int) dataMembers.size(), (int) arraylength, caps.limits[Graphics::LIMIT_TEXEL_BUFFER_SIZE]);
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
|
||||
@@ -48,6 +48,8 @@ public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
static const size_t SHADER_STORAGE_BUFFER_MAX_STRIDE = 2048;
|
||||
|
||||
enum MapType
|
||||
{
|
||||
MAP_WRITE_INVALIDATE,
|
||||
@@ -60,6 +62,7 @@ public:
|
||||
TYPEFLAG_INDEX = 1 << BUFFERTYPE_INDEX,
|
||||
TYPEFLAG_UNIFORM = 1 << BUFFERTYPE_UNIFORM,
|
||||
TYPEFLAG_TEXEL = 1 << BUFFERTYPE_TEXEL,
|
||||
TYPEFLAG_SHADER_STORAGE = 1 << BUFFERTYPE_SHADER_STORAGE,
|
||||
};
|
||||
|
||||
struct DataDeclaration
|
||||
|
||||
@@ -298,7 +298,7 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource)
|
||||
if (!validstages[i])
|
||||
continue;
|
||||
|
||||
if (info.isStage[i])
|
||||
if (info.stages[i] != Shader::ENTRYPOINT_NONE)
|
||||
{
|
||||
isanystage = true;
|
||||
stages[i].set(newShaderStage((ShaderStage::StageType) i, source, info), Acquire::NORETAIN);
|
||||
@@ -377,7 +377,7 @@ bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagess
|
||||
if (!validstages[i])
|
||||
continue;
|
||||
|
||||
if (info.isStage[i])
|
||||
if (info.stages[i] != Shader::ENTRYPOINT_NONE)
|
||||
{
|
||||
isanystage = true;
|
||||
std::string glsl = Shader::createShaderStageCode(this, stype, source, info);
|
||||
@@ -1987,15 +1987,16 @@ StringMap<Graphics::Feature, Graphics::FEATURE_MAX_ENUM> Graphics::features(Grap
|
||||
|
||||
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::systemLimitEntries[] =
|
||||
{
|
||||
{ "pointsize", LIMIT_POINT_SIZE },
|
||||
{ "texturesize", LIMIT_TEXTURE_SIZE },
|
||||
{ "texturelayers", LIMIT_TEXTURE_LAYERS },
|
||||
{ "volumetexturesize", LIMIT_VOLUME_TEXTURE_SIZE },
|
||||
{ "cubetexturesize", LIMIT_CUBE_TEXTURE_SIZE },
|
||||
{ "texelbuffersize", LIMIT_TEXEL_BUFFER_SIZE },
|
||||
{ "rendertargets", LIMIT_RENDER_TARGETS },
|
||||
{ "texturemsaa", LIMIT_TEXTURE_MSAA },
|
||||
{ "anisotropy", LIMIT_ANISOTROPY },
|
||||
{ "pointsize", LIMIT_POINT_SIZE },
|
||||
{ "texturesize", LIMIT_TEXTURE_SIZE },
|
||||
{ "texturelayers", LIMIT_TEXTURE_LAYERS },
|
||||
{ "volumetexturesize", LIMIT_VOLUME_TEXTURE_SIZE },
|
||||
{ "cubetexturesize", LIMIT_CUBE_TEXTURE_SIZE },
|
||||
{ "texelbuffersize", LIMIT_TEXEL_BUFFER_SIZE },
|
||||
{ "shaderstoragebuffersize", LIMIT_SHADER_STORAGE_BUFFER_SIZE },
|
||||
{ "rendertargets", LIMIT_RENDER_TARGETS },
|
||||
{ "texturemsaa", LIMIT_TEXTURE_MSAA },
|
||||
{ "anisotropy", LIMIT_ANISOTROPY },
|
||||
};
|
||||
|
||||
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM> Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries));
|
||||
|
||||
@@ -163,6 +163,7 @@ public:
|
||||
LIMIT_CUBE_TEXTURE_SIZE,
|
||||
LIMIT_TEXTURE_LAYERS,
|
||||
LIMIT_TEXEL_BUFFER_SIZE,
|
||||
LIMIT_SHADER_STORAGE_BUFFER_SIZE,
|
||||
LIMIT_RENDER_TARGETS,
|
||||
LIMIT_TEXTURE_MSAA,
|
||||
LIMIT_ANISOTROPY,
|
||||
|
||||
+159
-39
@@ -26,6 +26,9 @@
|
||||
// glslang
|
||||
#include "libraries/glslang/glslang/Public/ShaderLang.h"
|
||||
|
||||
// Needed for reflection information.
|
||||
#include "libraries/glslang/glslang/Include/Types.h"
|
||||
|
||||
// C++
|
||||
#include <string>
|
||||
#include <regex>
|
||||
@@ -38,6 +41,7 @@ namespace graphics
|
||||
|
||||
namespace glsl
|
||||
{
|
||||
|
||||
static const char global_syntax[] = R"(
|
||||
#if !defined(GL_ES) && __VERSION__ < 140
|
||||
#define lowp
|
||||
@@ -65,7 +69,11 @@ static const char global_syntax[] = R"(
|
||||
#define DepthCubeImage samplerCubeShadow
|
||||
#endif
|
||||
#define extern uniform
|
||||
#ifdef GL_EXT_texture_array
|
||||
#if defined(GL_EXT_texture_array) && (!defined(GL_ES) || __VERSION__ > 100 || defined(GL_OES_gpu_shader5))
|
||||
// Only used when !GLSLES1 to work around Ouya driver bug. But we still want it
|
||||
// enabled for glslang validation when glsl 1-on-3 is used, so also enable it if
|
||||
// OES_gpu_shader5 exists.
|
||||
#define LOVE_EXT_TEXTURE_ARRAY_ENABLED
|
||||
#extension GL_EXT_texture_array : enable
|
||||
#endif
|
||||
#ifdef GL_OES_texture_3D
|
||||
@@ -133,7 +141,7 @@ void love_initializeBuiltinUniforms() {
|
||||
|
||||
static const char global_functions[] = R"(
|
||||
#ifdef GL_ES
|
||||
#if __VERSION__ >= 300 || defined(GL_EXT_texture_array)
|
||||
#if __VERSION__ >= 300 || defined(LOVE_EXT_TEXTURE_ARRAY_ENABLED)
|
||||
precision lowp sampler2DArray;
|
||||
#endif
|
||||
#if __VERSION__ >= 300 || defined(GL_OES_texture_3D)
|
||||
@@ -169,7 +177,7 @@ static const char global_functions[] = R"(
|
||||
#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
|
||||
vec4 Texel(sampler3D s, vec3 c) { return love_texture3D(s, c); }
|
||||
#endif
|
||||
#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
|
||||
#if __VERSION__ >= 130 || defined(LOVE_EXT_TEXTURE_ARRAY_ENABLED)
|
||||
vec4 Texel(sampler2DArray s, vec3 c) { return love_texture2DArray(s, c); }
|
||||
#endif
|
||||
#ifdef PIXEL
|
||||
@@ -178,7 +186,7 @@ static const char global_functions[] = R"(
|
||||
#if __VERSION__ > 100 || defined(GL_OES_texture_3D)
|
||||
vec4 Texel(sampler3D s, vec3 c, float b) { return love_texture3D(s, c, b); }
|
||||
#endif
|
||||
#if __VERSION__ >= 130 || defined(GL_EXT_texture_array)
|
||||
#if __VERSION__ >= 130 || defined(LOVE_EXT_TEXTURE_ARRAY_ENABLED)
|
||||
vec4 Texel(sampler2DArray s, vec3 c, float b) { return love_texture2DArray(s, c, b); }
|
||||
#endif
|
||||
#endif
|
||||
@@ -282,6 +290,16 @@ void main() {
|
||||
}
|
||||
)";
|
||||
|
||||
static const char vertex_main_raw[] = R"(
|
||||
void vertexmain();
|
||||
|
||||
void main() {
|
||||
love_initializeBuiltinUniforms();
|
||||
setPointSize();
|
||||
vertexmain();
|
||||
}
|
||||
)";
|
||||
|
||||
static const char pixel_header[] = R"(
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
@@ -291,29 +309,10 @@ static const char pixel_header[] = R"(
|
||||
|
||||
#if __VERSION__ >= 130
|
||||
#define varying in
|
||||
// Some drivers seem to make the pixel shader do more work when multiple
|
||||
// pixel shader outputs are defined, even when only one is actually used.
|
||||
// TODO: We should use reflection or something instead of this, to determine
|
||||
// how many outputs are actually used in the shader code.
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
LOVE_IO_LOCATION(0) out vec4 love_RenderTargets[love_MaxRenderTargets];
|
||||
#define love_PixelColor love_RenderTargets[0]
|
||||
#else
|
||||
LOVE_IO_LOCATION(0) out vec4 love_PixelColor;
|
||||
#endif
|
||||
#else
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
#define love_RenderTargets gl_FragData
|
||||
#endif
|
||||
#define love_PixelColor gl_FragColor
|
||||
#endif
|
||||
|
||||
// Legacy
|
||||
#define love_MaxCanvases love_MaxRenderTargets
|
||||
#define love_Canvases love_RenderTargets
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
#define LOVE_MULTI_CANVASES 1
|
||||
#endif
|
||||
|
||||
// See Shader::updateScreenParams in Shader.cpp.
|
||||
#define love_PixelCoord (vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenSize.z) + love_ScreenSize.w))
|
||||
@@ -342,6 +341,12 @@ vec4 VideoTexel(vec2 texcoords) {
|
||||
)";
|
||||
|
||||
static const char pixel_main[] = R"(
|
||||
#if __VERSION__ >= 130
|
||||
LOVE_IO_LOCATION(0) out vec4 love_PixelColor;
|
||||
#else
|
||||
#define love_PixelColor gl_FragColor
|
||||
#endif
|
||||
|
||||
uniform sampler2D MainTex;
|
||||
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
|
||||
varying mediump vec4 VaryingColor;
|
||||
@@ -355,6 +360,30 @@ void main() {
|
||||
)";
|
||||
|
||||
static const char pixel_main_custom[] = R"(
|
||||
#if __VERSION__ >= 130
|
||||
// Some drivers seem to make the pixel shader do more work when multiple
|
||||
// pixel shader outputs are defined, even when only one is actually used.
|
||||
// TODO: We should use reflection or something instead of this, to determine
|
||||
// how many outputs are actually used in the shader code.
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
LOVE_IO_LOCATION(0) out vec4 love_RenderTargets[love_MaxRenderTargets];
|
||||
#define love_PixelColor love_RenderTargets[0]
|
||||
#else
|
||||
LOVE_IO_LOCATION(0) out vec4 love_PixelColor;
|
||||
#endif
|
||||
#else
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
#define love_RenderTargets gl_FragData
|
||||
#endif
|
||||
#define love_PixelColor gl_FragColor
|
||||
#endif
|
||||
|
||||
// Legacy
|
||||
#define love_Canvases love_RenderTargets
|
||||
#ifdef LOVE_MULTI_RENDER_TARGETS
|
||||
#define LOVE_MULTI_CANVASES 1
|
||||
#endif
|
||||
|
||||
varying LOVE_HIGHP_OR_MEDIUMP vec4 VaryingTexCoord;
|
||||
varying mediump vec4 VaryingColor;
|
||||
|
||||
@@ -366,6 +395,15 @@ void main() {
|
||||
}
|
||||
)";
|
||||
|
||||
static const char pixel_main_raw[] = R"(
|
||||
void pixelmain();
|
||||
|
||||
void main() {
|
||||
love_initializeBuiltinUniforms();
|
||||
pixelmain();
|
||||
}
|
||||
)";
|
||||
|
||||
struct StageInfo
|
||||
{
|
||||
const char *name;
|
||||
@@ -373,12 +411,13 @@ struct StageInfo
|
||||
const char *functions;
|
||||
const char *main;
|
||||
const char *main_custom;
|
||||
const char *main_raw;
|
||||
};
|
||||
|
||||
static const StageInfo stageInfo[] =
|
||||
{
|
||||
{ "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main },
|
||||
{ "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom },
|
||||
{ "VERTEX", vertex_header, vertex_functions, vertex_main, vertex_main, vertex_main_raw },
|
||||
{ "PIXEL", pixel_header, pixel_functions, pixel_main, pixel_main_custom, pixel_main_raw },
|
||||
};
|
||||
|
||||
static_assert((sizeof(stageInfo) / sizeof(StageInfo)) == ShaderStage::STAGE_MAX_ENUM, "Stages array size must match ShaderStage enum.");
|
||||
@@ -407,30 +446,38 @@ static Shader::Language getTargetLanguage(const std::string &src)
|
||||
return lang;
|
||||
}
|
||||
|
||||
static bool isVertexCode(const std::string &src)
|
||||
static Shader::EntryPoint getVertexEntryPoint(const std::string &src)
|
||||
{
|
||||
std::regex r("vec4\\s+position\\s*\\(");
|
||||
std::smatch m;
|
||||
return std::regex_search(src, m, r);
|
||||
|
||||
if (std::regex_search(src, m, std::regex("void\\s+vertexmain\\s*\\(")))
|
||||
return Shader::ENTRYPOINT_RAW;
|
||||
|
||||
if (std::regex_search(src, m, std::regex("vec4\\s+position\\s*\\(")))
|
||||
return Shader::ENTRYPOINT_HIGHLEVEL;
|
||||
|
||||
return Shader::ENTRYPOINT_NONE;
|
||||
}
|
||||
|
||||
static bool isPixelCode(const std::string &src, bool &custompixel, bool &mrt)
|
||||
static Shader::EntryPoint getPixelEntryPoint(const std::string &src, bool &mrt)
|
||||
{
|
||||
custompixel = false;
|
||||
mrt = false;
|
||||
std::smatch m;
|
||||
|
||||
if (std::regex_search(src, m, std::regex("void\\s+pixelmain\\s*\\(")))
|
||||
return Shader::ENTRYPOINT_RAW;
|
||||
|
||||
if (std::regex_search(src, m, std::regex("vec4\\s+effect\\s*\\(")))
|
||||
return true;
|
||||
return Shader::ENTRYPOINT_HIGHLEVEL;
|
||||
|
||||
if (std::regex_search(src, m, std::regex("void\\s+effect\\s*\\(")))
|
||||
{
|
||||
custompixel = true;
|
||||
if (src.find("love_RenderTargets") != std::string::npos || src.find("love_Canvases") != std::string::npos)
|
||||
mrt = true;
|
||||
return true;
|
||||
return Shader::ENTRYPOINT_CUSTOM;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Shader::ENTRYPOINT_NONE;
|
||||
}
|
||||
|
||||
} // glsl
|
||||
@@ -446,8 +493,8 @@ Shader::SourceInfo Shader::getSourceInfo(const std::string &src)
|
||||
{
|
||||
SourceInfo info = {};
|
||||
info.language = glsl::getTargetLanguage(src);
|
||||
info.isStage[ShaderStage::STAGE_VERTEX] = glsl::isVertexCode(src);
|
||||
info.isStage[ShaderStage::STAGE_PIXEL] = glsl::isPixelCode(src, info.customPixelFunction, info.usesMRT);
|
||||
info.stages[ShaderStage::STAGE_VERTEX] = glsl::getVertexEntryPoint(src);
|
||||
info.stages[ShaderStage::STAGE_PIXEL] = glsl::getPixelEntryPoint(src, info.usesMRT);
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -456,6 +503,12 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType
|
||||
if (info.language == Shader::LANGUAGE_MAX_ENUM)
|
||||
throw love::Exception("Invalid shader language");
|
||||
|
||||
if (info.stages[stage] == ENTRYPOINT_NONE)
|
||||
throw love::Exception("Cannot find entry point for shader stage.");
|
||||
|
||||
if (info.stages[stage] == ENTRYPOINT_RAW && info.language == LANGUAGE_GLSL1)
|
||||
throw love::Exception("Shaders using a raw entry point (vertexmain or pixelmain) must use GLSL 3 or greater.");
|
||||
|
||||
const auto &features = gfx->getCapabilities().features;
|
||||
|
||||
if (info.language == LANGUAGE_GLSL3 && !features[Graphics::FEATURE_GLSL3])
|
||||
@@ -490,7 +543,15 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStage::StageType
|
||||
ss << glsl::global_uniforms;
|
||||
ss << glsl::global_functions;
|
||||
ss << stageinfo.functions;
|
||||
ss << (info.customPixelFunction ? stageinfo.main_custom : stageinfo.main);
|
||||
|
||||
if (info.stages[stage] == ENTRYPOINT_HIGHLEVEL)
|
||||
ss << stageinfo.main;
|
||||
else if (info.stages[stage] == ENTRYPOINT_CUSTOM)
|
||||
ss << stageinfo.main_custom;
|
||||
else if (info.stages[stage] == ENTRYPOINT_RAW)
|
||||
ss << stageinfo.main_raw;
|
||||
else
|
||||
throw love::Exception("Unknown shader entry point %d", info.stages[stage]);
|
||||
ss << ((!gles && (lang == Shader::LANGUAGE_GLSL1 || glsl1on3)) ? "#line 0\n" : "#line 1\n");
|
||||
ss << code;
|
||||
|
||||
@@ -501,7 +562,7 @@ Shader::Shader(ShaderStage *vertex, ShaderStage *pixel)
|
||||
: stages()
|
||||
{
|
||||
std::string err;
|
||||
if (!validate(vertex, pixel, err))
|
||||
if (!validateInternal(vertex, pixel, err, validationReflection))
|
||||
throw love::Exception("%s", err.c_str());
|
||||
|
||||
stages[ShaderStage::STAGE_VERTEX] = vertex;
|
||||
@@ -584,7 +645,13 @@ void Shader::checkMainTexture(Texture *tex) const
|
||||
checkMainTextureType(tex->getTextureType(), tex->getSamplerState().depthSampleMode.hasValue);
|
||||
}
|
||||
|
||||
bool Shader::validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err)
|
||||
bool Shader::validate(ShaderStage* vertex, ShaderStage* pixel, std::string& err)
|
||||
{
|
||||
ValidationReflection reflection;
|
||||
return validateInternal(vertex, pixel, err, reflection);
|
||||
}
|
||||
|
||||
bool Shader::validateInternal(ShaderStage *vertex, ShaderStage *pixel, std::string &err, ValidationReflection &reflection)
|
||||
{
|
||||
glslang::TProgram program;
|
||||
|
||||
@@ -600,6 +667,59 @@ bool Shader::validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!program.buildReflection(EShReflectionSeparateBuffers))
|
||||
{
|
||||
err = "Cannot get reflection information for shader.";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < program.getNumBufferBlocks(); i++)
|
||||
{
|
||||
const glslang::TObjectReflection &info = program.getBufferBlock(i);
|
||||
const glslang::TType *type = info.getType();
|
||||
if (type != nullptr)
|
||||
{
|
||||
const glslang::TQualifier &qualifiers = type->getQualifier();
|
||||
|
||||
if ((!qualifiers.isReadOnly() || qualifiers.isWriteOnly()) && (info.stages & (EShLangVertexMask | EShLangFragmentMask)))
|
||||
{
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must be marked as readonly in vertex and pixel shaders.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (qualifiers.layoutPacking != glslang::ElpStd430)
|
||||
{
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must use the std430 packing layout.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const glslang::TTypeList *structure = type->getStruct();
|
||||
if (structure == nullptr || structure->size() != 1)
|
||||
{
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must contain a single unsized array of structs.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const glslang::TType* structtype = (*structure)[0].type;
|
||||
if (structtype == nullptr || structtype->getBasicType() != glslang::EbtStruct || !structtype->isUnsizedArray())
|
||||
{
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must contain a single unsized array of structs.";
|
||||
return false;
|
||||
}
|
||||
|
||||
BufferReflection bufferReflection = {};
|
||||
bufferReflection.stride = (size_t) info.size;
|
||||
bufferReflection.memberCount = (size_t) info.numMembers;
|
||||
|
||||
reflection.storageBuffers[info.name] = bufferReflection;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = "Shader validation error:\nCannot retrieve type information for Storage Buffer Block '" + info.name + "'.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ public:
|
||||
UNIFORM_BOOL,
|
||||
UNIFORM_SAMPLER,
|
||||
UNIFORM_TEXELBUFFER,
|
||||
UNIFORM_STORAGEBUFFER,
|
||||
UNIFORM_UNKNOWN,
|
||||
UNIFORM_MAX_ENUM
|
||||
};
|
||||
@@ -90,11 +91,18 @@ public:
|
||||
STANDARD_MAX_ENUM
|
||||
};
|
||||
|
||||
enum EntryPoint
|
||||
{
|
||||
ENTRYPOINT_NONE,
|
||||
ENTRYPOINT_HIGHLEVEL,
|
||||
ENTRYPOINT_CUSTOM,
|
||||
ENTRYPOINT_RAW,
|
||||
};
|
||||
|
||||
struct SourceInfo
|
||||
{
|
||||
Language language;
|
||||
bool isStage[ShaderStage::STAGE_MAX_ENUM];
|
||||
bool customPixelFunction;
|
||||
EntryPoint stages[ShaderStage::STAGE_MAX_ENUM];
|
||||
bool usesMRT;
|
||||
};
|
||||
|
||||
@@ -119,6 +127,8 @@ public:
|
||||
TextureType textureType;
|
||||
DataBaseType texelBufferType;
|
||||
bool isDepthSampler;
|
||||
size_t bufferStride;
|
||||
size_t bufferMemberCount;
|
||||
std::string name;
|
||||
|
||||
union
|
||||
@@ -220,8 +230,23 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
struct BufferReflection
|
||||
{
|
||||
size_t stride;
|
||||
size_t memberCount;
|
||||
};
|
||||
|
||||
struct ValidationReflection
|
||||
{
|
||||
std::map<std::string, BufferReflection> storageBuffers;
|
||||
};
|
||||
|
||||
static bool validateInternal(ShaderStage* vertex, ShaderStage* pixel, std::string& err, ValidationReflection &reflection);
|
||||
|
||||
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
|
||||
|
||||
ValidationReflection validationReflection;
|
||||
|
||||
}; // Shader
|
||||
|
||||
} // graphics
|
||||
|
||||
@@ -78,6 +78,8 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
|
||||
mapType = BUFFERTYPE_VERTEX;
|
||||
else if (typeFlags & TYPEFLAG_INDEX)
|
||||
mapType = BUFFERTYPE_INDEX;
|
||||
else if (typeFlags & TYPEFLAG_SHADER_STORAGE)
|
||||
mapType = BUFFERTYPE_SHADER_STORAGE;
|
||||
|
||||
target = OpenGL::getGLBufferType(mapType);
|
||||
|
||||
|
||||
@@ -333,8 +333,8 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
|
||||
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||
|
||||
// Set whether drawing converts input from linear -> sRGB colorspace.
|
||||
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|
||||
|| GLAD_ES_VERSION_3_0)
|
||||
if (!gl.bugs.brokenSRGB && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB
|
||||
|| GLAD_EXT_framebuffer_sRGB || GLAD_ES_VERSION_3_0))
|
||||
{
|
||||
if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
|
||||
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, isGammaCorrect());
|
||||
@@ -362,7 +362,19 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
|
||||
|
||||
const float texel[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
love::graphics::Buffer *buffer = newBuffer(settings, format, texel, sizeof(texel), 1);
|
||||
auto buffer = newBuffer(settings, format, texel, sizeof(texel), 1);
|
||||
defaultBuffers[BUFFERTYPE_TEXEL].set(buffer, Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
if (capabilities.features[FEATURE_GLSL4] && defaultBuffers[BUFFERTYPE_SHADER_STORAGE].get() == nullptr)
|
||||
{
|
||||
Buffer::Settings settings(Buffer::TYPEFLAG_SHADER_STORAGE, BUFFERUSAGE_STATIC);
|
||||
std::vector<Buffer::DataDeclaration> format = {{"", DATAFORMAT_FLOAT, 0}};
|
||||
|
||||
std::vector<float> data;
|
||||
data.resize(Buffer::SHADER_STORAGE_BUFFER_MAX_STRIDE / 4);
|
||||
|
||||
auto buffer = newBuffer(settings, format, data.data(), data.size() * sizeof(float), data.size());
|
||||
defaultBuffers[BUFFERTYPE_TEXEL].set(buffer, Acquire::NORETAIN);
|
||||
}
|
||||
|
||||
@@ -376,6 +388,9 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
|
||||
if (defaultBuffers[BUFFERTYPE_TEXEL].get())
|
||||
gl.setDefaultTexelBuffer((GLuint) defaultBuffers[BUFFERTYPE_TEXEL]->getTexelBufferHandle());
|
||||
|
||||
if (defaultBuffers[BUFFERTYPE_SHADER_STORAGE].get())
|
||||
gl.setDefaultStorageBuffer((GLuint) defaultBuffers[BUFFERTYPE_SHADER_STORAGE]->getHandle());
|
||||
|
||||
// Reload all volatile objects.
|
||||
if (!Volatile::loadAll())
|
||||
::printf("Could not reload all volatile objects.\n");
|
||||
@@ -1538,7 +1553,7 @@ void Graphics::initCapabilities()
|
||||
capabilities.features[FEATURE_GLSL3] = GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
|
||||
capabilities.features[FEATURE_GLSL4] = GLAD_ES_VERSION_3_1 || (gl.isCoreProfile() && GLAD_VERSION_4_3);
|
||||
capabilities.features[FEATURE_INSTANCING] = gl.isInstancingSupported();
|
||||
capabilities.features[FEATURE_TEXEL_BUFFER] = gl.areTexelBuffersSupported();
|
||||
capabilities.features[FEATURE_TEXEL_BUFFER] = gl.isBufferTypeSupported(BUFFERTYPE_TEXEL);
|
||||
static_assert(FEATURE_MAX_ENUM == 11, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
|
||||
|
||||
capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
|
||||
@@ -1547,10 +1562,11 @@ void Graphics::initCapabilities()
|
||||
capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = gl.getMax3DTextureSize();
|
||||
capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = gl.getMaxCubeTextureSize();
|
||||
capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = gl.getMaxTexelBufferSize();
|
||||
capabilities.limits[LIMIT_SHADER_STORAGE_BUFFER_SIZE] = gl.getMaxShaderStorageBufferSize();
|
||||
capabilities.limits[LIMIT_RENDER_TARGETS] = gl.getMaxRenderTargets();
|
||||
capabilities.limits[LIMIT_TEXTURE_MSAA] = gl.getMaxSamples();
|
||||
capabilities.limits[LIMIT_ANISOTROPY] = gl.getMaxAnisotropy();
|
||||
static_assert(LIMIT_MAX_ENUM == 9, "Graphics::initCapabilities must be updated when adding a new system limit!");
|
||||
static_assert(LIMIT_MAX_ENUM == 10, "Graphics::initCapabilities must be updated when adding a new system limit!");
|
||||
|
||||
for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
|
||||
capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
|
||||
|
||||
@@ -101,9 +101,12 @@ OpenGL::OpenGL()
|
||||
, max3DTextureSize(0)
|
||||
, maxCubeTextureSize(0)
|
||||
, maxTextureArrayLayers(0)
|
||||
, maxTexelBufferSize(0)
|
||||
, maxShaderStorageBufferSize(0)
|
||||
, maxRenderTargets(1)
|
||||
, maxSamples(1)
|
||||
, maxTextureUnits(1)
|
||||
, maxShaderStorageBufferBindings(0)
|
||||
, maxPointSize(1)
|
||||
, coreProfile(false)
|
||||
, vendor(VENDOR_UNKNOWN)
|
||||
@@ -150,6 +153,16 @@ bool OpenGL::initContext()
|
||||
if (strstr(device, "HD Graphics 4000") || strstr(device, "HD Graphics 2500"))
|
||||
bugs.clientWaitSyncStalls = true;
|
||||
}
|
||||
|
||||
if (getVendor() == VENDOR_INTEL)
|
||||
{
|
||||
const char *device = (const char *) glGetString(GL_RENDERER);
|
||||
if (strstr(device, "HD Graphics 3000") || strstr(device, "HD Graphics 2000")
|
||||
|| !strcmp(device, "Intel(R) HD Graphics") || !strcmp(device, "Intel(R) HD Graphics Family"))
|
||||
{
|
||||
bugs.brokenSRGB = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LOVE_WINDOWS
|
||||
@@ -210,8 +223,8 @@ void OpenGL::setupContext()
|
||||
setEnableState(ENABLE_SCISSOR_TEST, state.enableState[ENABLE_SCISSOR_TEST]);
|
||||
setEnableState(ENABLE_FACE_CULL, state.enableState[ENABLE_FACE_CULL]);
|
||||
|
||||
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
|
||||
|| GLAD_EXT_sRGB_write_control)
|
||||
if (!bugs.brokenSRGB && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB
|
||||
|| GLAD_EXT_framebuffer_sRGB || GLAD_EXT_sRGB_write_control))
|
||||
{
|
||||
setEnableState(ENABLE_FRAMEBUFFER_SRGB, state.enableState[ENABLE_FRAMEBUFFER_SRGB]);
|
||||
}
|
||||
@@ -225,9 +238,13 @@ void OpenGL::setupContext()
|
||||
for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
|
||||
{
|
||||
state.boundBuffers[i] = 0;
|
||||
glBindBuffer(getGLBufferType((BufferType) i), 0);
|
||||
if (isBufferTypeSupported((BufferType) i))
|
||||
glBindBuffer(getGLBufferType((BufferType) i), 0);
|
||||
}
|
||||
|
||||
if (isBufferTypeSupported(BUFFERTYPE_SHADER_STORAGE))
|
||||
state.boundIndexedBuffers[BUFFERTYPE_SHADER_STORAGE].resize(maxShaderStorageBufferBindings, 0);
|
||||
|
||||
// Initialize multiple texture unit support for shaders.
|
||||
for (int i = 0; i < TEXTURE_MAX_ENUM + 1; i++)
|
||||
{
|
||||
@@ -280,10 +297,6 @@ void OpenGL::deInitContext()
|
||||
}
|
||||
}
|
||||
|
||||
if (state.defaultTexelBuffer != 0)
|
||||
gl.deleteTexture(state.defaultTexelBuffer);
|
||||
state.defaultTexelBuffer = 0;
|
||||
|
||||
contextInitialized = false;
|
||||
}
|
||||
|
||||
@@ -391,15 +404,23 @@ void OpenGL::initOpenGLFunctions()
|
||||
}
|
||||
}
|
||||
|
||||
if (GLAD_ES_VERSION_2_0 && GLAD_OES_texture_3D && !GLAD_ES_VERSION_3_0)
|
||||
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
|
||||
{
|
||||
// Function signatures don't match, we'll have to conditionally call it
|
||||
//fp_glTexImage3D = fp_glTexImage3DOES;
|
||||
fp_glTexSubImage3D = fp_glTexSubImage3DOES;
|
||||
fp_glCopyTexSubImage3D = fp_glCopyTexSubImage3DOES;
|
||||
fp_glCompressedTexImage3D = fp_glCompressedTexImage3DOES;
|
||||
fp_glCompressedTexSubImage3D = fp_glCompressedTexSubImage3DOES;
|
||||
fp_glFramebufferTexture3D = fp_glFramebufferTexture3DOES;
|
||||
// The Nvidia Tegra 3 driver (used by Ouya) claims to support GL_EXT_texture_array but
|
||||
// segfaults if you actually try to use it. OpenGL ES 2.0 devices should use OES_texture_3D.
|
||||
// GL_EXT_texture_array is for desktops.
|
||||
GLAD_EXT_texture_array = false;
|
||||
|
||||
if (GLAD_OES_texture_3D)
|
||||
{
|
||||
// Function signatures don't match, we'll have to conditionally call it
|
||||
//fp_glTexImage3D = fp_glTexImage3DOES;
|
||||
fp_glTexSubImage3D = fp_glTexSubImage3DOES;
|
||||
fp_glCopyTexSubImage3D = fp_glCopyTexSubImage3DOES;
|
||||
fp_glCompressedTexImage3D = fp_glCompressedTexImage3DOES;
|
||||
fp_glCompressedTexSubImage3D = fp_glCompressedTexSubImage3DOES;
|
||||
fp_glFramebufferTexture3D = fp_glFramebufferTexture3DOES;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GLAD_VERSION_3_2 && !GLAD_ES_VERSION_3_2 && !GLAD_ARB_draw_elements_base_vertex)
|
||||
@@ -463,11 +484,22 @@ void OpenGL::initMaxValues()
|
||||
else
|
||||
maxTextureArrayLayers = 0;
|
||||
|
||||
if (areTexelBuffersSupported())
|
||||
if (isBufferTypeSupported(BUFFERTYPE_TEXEL))
|
||||
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTexelBufferSize);
|
||||
else
|
||||
maxTexelBufferSize = 0;
|
||||
|
||||
if (isBufferTypeSupported(BUFFERTYPE_SHADER_STORAGE))
|
||||
{
|
||||
glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &maxShaderStorageBufferSize);
|
||||
glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxShaderStorageBufferBindings);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxShaderStorageBufferSize = 0;
|
||||
maxShaderStorageBufferBindings = 0;
|
||||
}
|
||||
|
||||
int maxattachments = 1;
|
||||
int maxdrawbuffers = 1;
|
||||
|
||||
@@ -589,6 +621,7 @@ GLenum OpenGL::getGLBufferType(BufferType type)
|
||||
case BUFFERTYPE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
|
||||
case BUFFERTYPE_TEXEL: return GL_TEXTURE_BUFFER;
|
||||
case BUFFERTYPE_UNIFORM: return GL_UNIFORM_BUFFER;
|
||||
case BUFFERTYPE_SHADER_STORAGE: return GL_SHADER_STORAGE_BUFFER;
|
||||
case BUFFERTYPE_MAX_ENUM: return GL_ZERO;
|
||||
}
|
||||
|
||||
@@ -786,6 +819,12 @@ void OpenGL::deleteBuffer(GLuint buffer)
|
||||
{
|
||||
if (state.boundBuffers[i] == buffer)
|
||||
state.boundBuffers[i] = 0;
|
||||
|
||||
for (GLuint &bufferid : state.boundIndexedBuffers[i])
|
||||
{
|
||||
if (bufferid == buffer)
|
||||
bufferid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1125,6 +1164,19 @@ void OpenGL::bindTextureToUnit(Texture *texture, int textureunit, bool restorepr
|
||||
bindTextureToUnit(textype, handle, textureunit, restoreprev, bindforedit);
|
||||
}
|
||||
|
||||
void OpenGL::bindIndexedBuffer(GLuint buffer, BufferType type, int index)
|
||||
{
|
||||
auto &bindings = state.boundIndexedBuffers[type];
|
||||
if (bindings.size() > (size_t) index && buffer != bindings[index])
|
||||
{
|
||||
bindings[index] = buffer;
|
||||
glBindBufferBase(getGLBufferType(type), index, buffer);
|
||||
|
||||
// glBindBufferBase affects glBindBuffer as well... for some reason.
|
||||
state.boundBuffers[type] = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::deleteTexture(GLuint texture)
|
||||
{
|
||||
// glDeleteTextures binds texture 0 to all texture units the deleted texture
|
||||
@@ -1375,9 +1427,28 @@ bool OpenGL::isTextureTypeSupported(TextureType type) const
|
||||
return GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_array;
|
||||
case TEXTURE_CUBE:
|
||||
return GLAD_VERSION_1_3 || GLAD_ES_VERSION_2_0;
|
||||
default:
|
||||
case TEXTURE_MAX_ENUM:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenGL::isBufferTypeSupported(BufferType type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BUFFERTYPE_VERTEX:
|
||||
case BUFFERTYPE_INDEX:
|
||||
return true;
|
||||
case BUFFERTYPE_TEXEL:
|
||||
// Not supported in ES until 3.2, which we don't support shaders for...
|
||||
return GLAD_VERSION_3_1;
|
||||
case BUFFERTYPE_SHADER_STORAGE:
|
||||
return (GLAD_VERSION_4_3 && isCoreProfile()) || GLAD_ES_VERSION_3_1;
|
||||
case BUFFERTYPE_MAX_ENUM:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenGL::isClampZeroOneTextureWrapSupported() const
|
||||
@@ -1418,12 +1489,6 @@ bool OpenGL::isMultiFormatMRTSupported() const
|
||||
return getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
|
||||
}
|
||||
|
||||
bool OpenGL::areTexelBuffersSupported() const
|
||||
{
|
||||
// Not supported in ES until 3.2, which we don't support shaders for...
|
||||
return GLAD_VERSION_3_1;
|
||||
}
|
||||
|
||||
int OpenGL::getMax2DTextureSize() const
|
||||
{
|
||||
return std::max(max2DTextureSize, 1);
|
||||
@@ -1449,6 +1514,11 @@ int OpenGL::getMaxTexelBufferSize() const
|
||||
return maxTexelBufferSize;
|
||||
}
|
||||
|
||||
int OpenGL::getMaxShaderStorageBufferSize() const
|
||||
{
|
||||
return maxShaderStorageBufferSize;
|
||||
}
|
||||
|
||||
int OpenGL::getMaxRenderTargets() const
|
||||
{
|
||||
return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS);
|
||||
@@ -1464,6 +1534,11 @@ int OpenGL::getMaxTextureUnits() const
|
||||
return maxTextureUnits;
|
||||
}
|
||||
|
||||
int OpenGL::getMaxShaderStorageBufferBindings() const
|
||||
{
|
||||
return maxShaderStorageBufferBindings;
|
||||
}
|
||||
|
||||
float OpenGL::getMaxPointSize() const
|
||||
{
|
||||
return maxPointSize;
|
||||
@@ -1882,6 +1957,8 @@ bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget,
|
||||
else
|
||||
return true;
|
||||
case PIXELFORMAT_RGBA8_UNORM_sRGB:
|
||||
if (gl.bugs.brokenSRGB)
|
||||
return false;
|
||||
if (rendertarget)
|
||||
{
|
||||
if (GLAD_VERSION_1_0)
|
||||
|
||||
@@ -178,6 +178,13 @@ public:
|
||||
**/
|
||||
bool brokenR8PixelFormat;
|
||||
|
||||
/**
|
||||
* Intel HD Graphics drivers on Windows prior to the HD 2500/4000 have
|
||||
* completely broken sRGB support.
|
||||
* https://github.com/love2d/love/issues/1592
|
||||
**/
|
||||
bool brokenSRGB;
|
||||
|
||||
/**
|
||||
* Other bugs which have workarounds that don't use conditional code at
|
||||
* the moment:
|
||||
@@ -312,6 +319,9 @@ public:
|
||||
GLuint getDefaultTexelBuffer() const { return state.defaultTexelBuffer; }
|
||||
void setDefaultTexelBuffer(GLuint tex) { state.defaultTexelBuffer = tex; }
|
||||
|
||||
GLuint getDefaultStorageBuffer() const { return state.defaultStorageBuffer; }
|
||||
void setDefaultStorageBuffer(GLuint buf) { state.defaultStorageBuffer = buf; }
|
||||
|
||||
/**
|
||||
* Helper for setting the active texture unit.
|
||||
*
|
||||
@@ -331,6 +341,8 @@ public:
|
||||
|
||||
void bindBufferTextureToUnit(GLuint texture, int textureunit, bool restoreprev, bool bindforedit);
|
||||
|
||||
void bindIndexedBuffer(GLuint buffer, BufferType type, int index);
|
||||
|
||||
/**
|
||||
* Helper for deleting an OpenGL texture.
|
||||
* Cleans up if the texture is currently bound.
|
||||
@@ -350,6 +362,7 @@ public:
|
||||
bool rawTexStorage(TextureType target, int levels, PixelFormat pixelformat, bool &isSRGB, int width, int height, int depth = 1);
|
||||
|
||||
bool isTextureTypeSupported(TextureType type) const;
|
||||
bool isBufferTypeSupported(BufferType type) const;
|
||||
bool isClampZeroOneTextureWrapSupported() const;
|
||||
bool isPixelShaderHighpSupported() const;
|
||||
bool isInstancingSupported() const;
|
||||
@@ -357,7 +370,6 @@ public:
|
||||
bool isSamplerLODBiasSupported() const;
|
||||
bool isBaseVertexSupported() const;
|
||||
bool isMultiFormatMRTSupported() const;
|
||||
bool areTexelBuffersSupported() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum supported width or height of a texture.
|
||||
@@ -372,6 +384,11 @@ public:
|
||||
**/
|
||||
int getMaxTexelBufferSize() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum number of bytes in a shader storage buffer.
|
||||
**/
|
||||
int getMaxShaderStorageBufferSize() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum supported number of simultaneous render targets.
|
||||
**/
|
||||
@@ -387,6 +404,11 @@ public:
|
||||
**/
|
||||
int getMaxTextureUnits() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum number of shader storage buffer bindings.
|
||||
**/
|
||||
int getMaxShaderStorageBufferBindings() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum point size.
|
||||
**/
|
||||
@@ -451,9 +473,11 @@ private:
|
||||
int maxCubeTextureSize;
|
||||
int maxTextureArrayLayers;
|
||||
int maxTexelBufferSize;
|
||||
int maxShaderStorageBufferSize;
|
||||
int maxRenderTargets;
|
||||
int maxSamples;
|
||||
int maxTextureUnits;
|
||||
int maxShaderStorageBufferBindings;
|
||||
float maxPointSize;
|
||||
|
||||
bool coreProfile;
|
||||
@@ -468,6 +492,8 @@ private:
|
||||
// Texture unit state (currently bound texture for each texture unit.)
|
||||
std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM + 1];
|
||||
|
||||
std::vector<GLuint> boundIndexedBuffers[BUFFERTYPE_MAX_ENUM];
|
||||
|
||||
bool enableState[ENABLE_MAX_ENUM];
|
||||
|
||||
GLenum faceCullMode;
|
||||
@@ -488,6 +514,7 @@ private:
|
||||
|
||||
GLuint defaultTexture[TEXTURE_MAX_ENUM];
|
||||
GLuint defaultTexelBuffer;
|
||||
GLuint defaultStorageBuffer;
|
||||
|
||||
} state;
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
static bool isBuffer(Shader::UniformType utype)
|
||||
{
|
||||
return utype == Shader::UNIFORM_TEXELBUFFER || utype == Shader::UNIFORM_STORAGEBUFFER;
|
||||
}
|
||||
|
||||
Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
|
||||
: love::graphics::Shader(vertex, pixel)
|
||||
, program(0)
|
||||
@@ -69,7 +74,7 @@ Shader::~Shader()
|
||||
|
||||
delete[] p.second.textures;
|
||||
}
|
||||
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
|
||||
else if (isBuffer(p.second.baseType))
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
@@ -195,7 +200,7 @@ void Shader::mapActiveUniforms()
|
||||
u.data = malloc(u.dataSize);
|
||||
break;
|
||||
case UNIFORM_MATRIX:
|
||||
u.dataSize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count;
|
||||
u.dataSize = sizeof(float) * ((size_t)u.matrix.rows * u.matrix.columns) * u.count;
|
||||
u.data = malloc(u.dataSize);
|
||||
break;
|
||||
default:
|
||||
@@ -267,7 +272,7 @@ void Shader::mapActiveUniforms()
|
||||
break;
|
||||
case UNIFORM_MATRIX:
|
||||
glGetUniformfv(program, location, &u.floats[offset]);
|
||||
offset += u.matrix.rows * u.matrix.columns;
|
||||
offset += (size_t)u.matrix.rows * u.matrix.columns;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -310,13 +315,90 @@ void Shader::mapActiveUniforms()
|
||||
}
|
||||
}
|
||||
|
||||
if (gl.isBufferTypeSupported(BUFFERTYPE_SHADER_STORAGE))
|
||||
{
|
||||
GLint numstoragebuffers = 0;
|
||||
glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &numstoragebuffers);
|
||||
|
||||
char namebuffer[2048] = { '\0' };
|
||||
|
||||
for (int sindex = 0; sindex < numstoragebuffers; sindex++)
|
||||
{
|
||||
UniformInfo u = {};
|
||||
u.baseType = UNIFORM_STORAGEBUFFER;
|
||||
|
||||
GLsizei namelength = 0;
|
||||
glGetProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, sindex, 2048, &namelength, namebuffer);
|
||||
|
||||
u.name = std::string(namebuffer, namelength);
|
||||
u.count = 1;
|
||||
|
||||
const auto reflectionit = validationReflection.storageBuffers.find(u.name);
|
||||
if (reflectionit != validationReflection.storageBuffers.end())
|
||||
{
|
||||
u.bufferStride = reflectionit->second.stride;
|
||||
u.bufferMemberCount = reflectionit->second.memberCount;
|
||||
}
|
||||
|
||||
// Make sure previously set uniform data is preserved, and shader-
|
||||
// initialized values are retrieved.
|
||||
auto oldu = olduniforms.find(u.name);
|
||||
if (oldu != olduniforms.end())
|
||||
{
|
||||
u.data = oldu->second.data;
|
||||
u.dataSize = oldu->second.dataSize;
|
||||
u.buffers = oldu->second.buffers;
|
||||
}
|
||||
else
|
||||
{
|
||||
u.dataSize = sizeof(int) * 1;
|
||||
u.data = malloc(u.dataSize);
|
||||
|
||||
u.ints[0] = -1;
|
||||
|
||||
u.buffers = new love::graphics::Buffer * [u.count];
|
||||
memset(u.buffers, 0, sizeof(Buffer*)* u.count);
|
||||
}
|
||||
|
||||
GLenum props[] = { GL_BUFFER_BINDING };
|
||||
glGetProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, sindex, 1, props, 1, nullptr, u.ints);
|
||||
|
||||
BufferBinding binding;
|
||||
binding.bindingindex = u.ints[0];
|
||||
binding.buffer = gl.getDefaultStorageBuffer();
|
||||
|
||||
if (binding.bindingindex >= 0)
|
||||
{
|
||||
int activeindex = (int)activeStorageBufferBindings.size();
|
||||
|
||||
storageBufferBindingIndexToActiveBinding[binding.bindingindex] = activeindex;
|
||||
|
||||
activeStorageBufferBindings.push_back(binding);
|
||||
}
|
||||
|
||||
uniforms[u.name] = u;
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
if (u.buffers[i] == nullptr)
|
||||
continue;
|
||||
Volatile* v = dynamic_cast<Volatile*>(u.buffers[i]);
|
||||
if (v != nullptr)
|
||||
v->loadVolatile();
|
||||
}
|
||||
|
||||
sendBuffers(&u, u.buffers, u.count, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure uniforms that existed before but don't exist anymore are
|
||||
// cleaned up. This theoretically shouldn't happen, but...
|
||||
for (const auto &p : olduniforms)
|
||||
{
|
||||
if (uniforms.find(p.first) == uniforms.end())
|
||||
{
|
||||
free(p.second.data);
|
||||
if (p.second.data != nullptr)
|
||||
free(p.second.data);
|
||||
|
||||
if (p.second.baseType == UNIFORM_SAMPLER)
|
||||
{
|
||||
@@ -328,7 +410,7 @@ void Shader::mapActiveUniforms()
|
||||
|
||||
delete[] p.second.textures;
|
||||
}
|
||||
else if (p.second.baseType == UNIFORM_TEXELBUFFER)
|
||||
else if (isBuffer(p.second.baseType))
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
@@ -354,6 +436,9 @@ bool Shader::loadVolatile()
|
||||
textureUnits.clear();
|
||||
textureUnits.push_back(TextureUnit());
|
||||
|
||||
storageBufferBindingIndexToActiveBinding.resize(gl.getMaxShaderStorageBufferBindings(), -1);
|
||||
activeStorageBufferBindings.clear();
|
||||
|
||||
for (const auto &stage : stages)
|
||||
{
|
||||
if (stage.get() != nullptr)
|
||||
@@ -491,6 +576,9 @@ void Shader::attach()
|
||||
}
|
||||
}
|
||||
|
||||
for (auto bufferbinding : activeStorageBufferBindings)
|
||||
gl.bindIndexedBuffer(bufferbinding.buffer, BUFFERTYPE_SHADER_STORAGE, bufferbinding.bindingindex);
|
||||
|
||||
// send any pending uniforms to the shader program.
|
||||
for (const auto &p : pendingUniformUpdates)
|
||||
updateUniform(p.first, p.second, true);
|
||||
@@ -713,10 +801,18 @@ static bool isTexelBufferTypeCompatible(DataBaseType a, DataBaseType b)
|
||||
|
||||
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalUpdate)
|
||||
{
|
||||
if (info->baseType != UNIFORM_TEXELBUFFER)
|
||||
return;
|
||||
uint32 requiredtypeflags = 0;
|
||||
|
||||
uint32 requiredtypeflags = Buffer::TYPEFLAG_TEXEL;
|
||||
bool texelbinding = info->baseType == UNIFORM_TEXELBUFFER;
|
||||
bool storagebinding = info->baseType == UNIFORM_STORAGEBUFFER;
|
||||
|
||||
if (texelbinding)
|
||||
requiredtypeflags = Buffer::TYPEFLAG_TEXEL;
|
||||
else if (storagebinding)
|
||||
requiredtypeflags = Buffer::TYPEFLAG_SHADER_STORAGE;
|
||||
|
||||
if (requiredtypeflags == 0)
|
||||
return;
|
||||
|
||||
bool shaderactive = current == this;
|
||||
|
||||
@@ -736,17 +832,43 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
||||
{
|
||||
if (internalUpdate)
|
||||
continue;
|
||||
else
|
||||
else if (texelbinding)
|
||||
throw love::Exception("Shader uniform '%s' is a texel buffer, but the given Buffer was not created with texel buffer capabilities.", info->name.c_str());
|
||||
else if (storagebinding)
|
||||
throw love::Exception("Shader uniform '%s' is a shader storage buffer block, but the given Buffer was not created with shader storage buffer capabilities.", info->name.c_str());
|
||||
else
|
||||
throw love::Exception("Shader uniform '%s' does not match the types supported by the given Buffer.", info->name.c_str());
|
||||
}
|
||||
|
||||
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
|
||||
if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
|
||||
if (texelbinding)
|
||||
{
|
||||
if (internalUpdate)
|
||||
continue;
|
||||
else
|
||||
throw love::Exception("Texel buffer's data format base type must match the variable declared in the shader.");
|
||||
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
|
||||
if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
|
||||
{
|
||||
if (internalUpdate)
|
||||
continue;
|
||||
else
|
||||
throw love::Exception("Texel buffer's data format base type must match the variable declared in the shader.");
|
||||
}
|
||||
}
|
||||
else if (storagebinding)
|
||||
{
|
||||
if (info->bufferStride != buffer->getArrayStride())
|
||||
{
|
||||
if (internalUpdate)
|
||||
continue;
|
||||
else
|
||||
throw love::Exception("Shader storage block '%s' has an array stride of %d bytes, but the given Buffer has an array stride of %d bytes.",
|
||||
info->name.c_str(), info->bufferStride, buffer->getArrayStride());
|
||||
}
|
||||
else if (info->bufferMemberCount != buffer->getDataMembers().size())
|
||||
{
|
||||
if (internalUpdate)
|
||||
continue;
|
||||
else
|
||||
throw love::Exception("Shader storage block '%s' has a struct with %d fields, but the given Buffer has a format with %d members.",
|
||||
info->name.c_str(), info->bufferMemberCount, buffer->getDataMembers().size());
|
||||
}
|
||||
}
|
||||
|
||||
buffer->retain();
|
||||
@@ -757,19 +879,39 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
||||
|
||||
info->buffers[i] = buffer;
|
||||
|
||||
GLuint gltex = 0;
|
||||
if (buffers[i] != nullptr)
|
||||
gltex = (GLuint) buffer->getTexelBufferHandle();
|
||||
else
|
||||
gltex = gl.getDefaultTexelBuffer();
|
||||
if (texelbinding)
|
||||
{
|
||||
GLuint gltex = 0;
|
||||
if (buffers[i] != nullptr)
|
||||
gltex = (GLuint) buffer->getTexelBufferHandle();
|
||||
else
|
||||
gltex = gl.getDefaultTexelBuffer();
|
||||
|
||||
int texunit = info->ints[i];
|
||||
int texunit = info->ints[i];
|
||||
|
||||
if (shaderactive)
|
||||
gl.bindBufferTextureToUnit(gltex, texunit, false, false);
|
||||
if (shaderactive)
|
||||
gl.bindBufferTextureToUnit(gltex, texunit, false, false);
|
||||
|
||||
// Store texture id so it can be re-bound to the texture unit later.
|
||||
textureUnits[texunit].texture = gltex;
|
||||
// Store texture id so it can be re-bound to the texture unit later.
|
||||
textureUnits[texunit].texture = gltex;
|
||||
}
|
||||
else if (storagebinding)
|
||||
{
|
||||
int bindingindex = info->ints[i];
|
||||
|
||||
GLuint glbuffer = 0;
|
||||
if (buffers[i] != nullptr)
|
||||
glbuffer = (GLuint) buffer->getHandle();
|
||||
else
|
||||
glbuffer = gl.getDefaultStorageBuffer();
|
||||
|
||||
if (shaderactive)
|
||||
gl.bindIndexedBuffer(glbuffer, BUFFERTYPE_SHADER_STORAGE, bindingindex);
|
||||
|
||||
int activeindex = storageBufferBindingIndexToActiveBinding[bindingindex];
|
||||
if (activeindex >= 0)
|
||||
activeStorageBufferBindings[activeindex].buffer = glbuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,12 @@ private:
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
struct BufferBinding
|
||||
{
|
||||
int bindingindex = 0;
|
||||
GLuint buffer = 0;
|
||||
};
|
||||
|
||||
// Map active uniform names to their locations.
|
||||
void mapActiveUniforms();
|
||||
|
||||
@@ -114,6 +120,9 @@ private:
|
||||
// Texture unit pool for setting textures
|
||||
std::vector<TextureUnit> textureUnits;
|
||||
|
||||
std::vector<int> storageBufferBindingIndexToActiveBinding;
|
||||
std::vector<BufferBinding> activeStorageBufferBindings;
|
||||
|
||||
std::vector<std::pair<const UniformInfo *, int>> pendingUniformUpdates;
|
||||
|
||||
float lastPointSize;
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers, bool clear)
|
||||
static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int mips, int layers, bool clear)
|
||||
{
|
||||
// get currently bound fbo to reset to it later
|
||||
GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
|
||||
@@ -42,11 +42,6 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer);
|
||||
|
||||
// Intel driver bug: https://github.com/love2d/love/issues/1592
|
||||
bool current_srgb = gl.isStateEnabled(OpenGL::ENABLE_FRAMEBUFFER_SRGB);
|
||||
if (current_srgb && isPixelFormatDepthStencil(format))
|
||||
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, false);
|
||||
|
||||
if (texture != 0)
|
||||
{
|
||||
if (isPixelFormatDepthStencil(format) && (GLAD_ES_VERSION_3_0 || !GLAD_ES_VERSION_2_0))
|
||||
@@ -68,37 +63,40 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
|
||||
// Make sure all faces and layers of the texture are initialized to
|
||||
// transparent black. This is unfortunately probably pretty slow for
|
||||
// 2D-array and 3D textures with a lot of layers...
|
||||
for (int layer = layers - 1; layer >= 0; layer--)
|
||||
for (int mip = mips - 1; mip >= 0; mip--)
|
||||
{
|
||||
for (int face = faces - 1; face >= 0; face--)
|
||||
for (int layer = layers - 1; layer >= 0; layer--)
|
||||
{
|
||||
for (GLenum attachment : fmt.framebufferAttachments)
|
||||
for (int face = faces - 1; face >= 0; face--)
|
||||
{
|
||||
if (attachment == GL_NONE)
|
||||
continue;
|
||||
|
||||
gl.framebufferTexture(attachment, texType, texture, 0, layer, face);
|
||||
}
|
||||
|
||||
if (clear)
|
||||
{
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
for (GLenum attachment : fmt.framebufferAttachments)
|
||||
{
|
||||
bool hadDepthWrites = gl.hasDepthWrites();
|
||||
if (!hadDepthWrites) // glDepthMask also affects glClear.
|
||||
gl.setDepthWrites(true);
|
||||
if (attachment == GL_NONE)
|
||||
continue;
|
||||
|
||||
gl.clearDepth(1.0);
|
||||
glClearStencil(0);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
if (!hadDepthWrites)
|
||||
gl.setDepthWrites(hadDepthWrites);
|
||||
gl.framebufferTexture(attachment, texType, texture, mip, layer, face);
|
||||
}
|
||||
else
|
||||
|
||||
if (clear)
|
||||
{
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
{
|
||||
bool hadDepthWrites = gl.hasDepthWrites();
|
||||
if (!hadDepthWrites) // glDepthMask also affects glClear.
|
||||
gl.setDepthWrites(true);
|
||||
|
||||
gl.clearDepth(1.0);
|
||||
glClearStencil(0);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
if (!hadDepthWrites)
|
||||
gl.setDepthWrites(hadDepthWrites);
|
||||
}
|
||||
else
|
||||
{
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,10 +107,6 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
|
||||
|
||||
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
|
||||
|
||||
// Restore sRGB state if we turned it off above.
|
||||
if (current_srgb && isPixelFormatDepthStencil(format))
|
||||
gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, current_srgb);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -328,28 +322,43 @@ void Texture::createTexture()
|
||||
|
||||
bool hasdata = slices.get(0, 0) != nullptr;
|
||||
|
||||
// All mipmap levels need to be initialized - for color formats we can clear
|
||||
// the base mip and use glGenerateMipmap after that's done. Depth and
|
||||
// stencil formats don't always support glGenerateMipmap so we need to
|
||||
// individually clear each mip level in that case. We avoid doing that for
|
||||
// color formats because of an Intel driver bug:
|
||||
// https://github.com/love2d/love/issues/1585
|
||||
int clearmips = 1;
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
clearmips = mipmapCount;
|
||||
|
||||
// Create a local FBO used for glReadPixels as well as MSAA blitting.
|
||||
if (isRenderTarget())
|
||||
{
|
||||
bool clear = !hasdata;
|
||||
int slices = texType == TEXTURE_VOLUME ? depth : layers;
|
||||
framebufferStatus = createFBO(fbo, texType, format, texture, slices, clear);
|
||||
framebufferStatus = createFBO(fbo, texType, format, texture, clearmips, slices, clear);
|
||||
}
|
||||
else if (!hasdata)
|
||||
{
|
||||
// Initialize all slices to transparent black.
|
||||
std::vector<uint8> emptydata(getPixelFormatSliceSize(format, w, h));
|
||||
for (int mip = 0; mip < clearmips; mip++)
|
||||
{
|
||||
int mipw = getPixelWidth(mip);
|
||||
int miph = getPixelHeight(mip);
|
||||
std::vector<uint8> emptydata(getPixelFormatSliceSize(format, mipw, miph));
|
||||
|
||||
Rect r = {0, 0, w, h};
|
||||
int slices = texType == TEXTURE_VOLUME ? depth : layers;
|
||||
slices = texType == TEXTURE_CUBE ? 6 : slices;
|
||||
for (int i = 0; i < slices; i++)
|
||||
uploadByteData(format, emptydata.data(), emptydata.size(), 0, i, r);
|
||||
Rect r = {0, 0, mipw, miph};
|
||||
int slices = texType == TEXTURE_VOLUME ? getDepth(mip) : layers;
|
||||
slices = texType == TEXTURE_CUBE ? 6 : slices;
|
||||
for (int i = 0; i < slices; i++)
|
||||
uploadByteData(format, emptydata.data(), emptydata.size(), mip, i, r);
|
||||
}
|
||||
}
|
||||
|
||||
// Non-readable textures can't have mipmaps (enforced in the base class),
|
||||
// so generateMipmaps here is fine - when they aren't already initialized.
|
||||
if (getMipmapCount() > 1 && slices.getMipmapCount() <= 1)
|
||||
if (clearmips < mipmapCount && slices.getMipmapCount() <= 1 && getMipmapsMode() != MIPMAPS_NONE)
|
||||
generateMipmaps();
|
||||
}
|
||||
|
||||
|
||||
@@ -339,9 +339,10 @@ const char *getConstant(BuiltinVertexAttribute attrib)
|
||||
|
||||
STRINGMAP_BEGIN(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName)
|
||||
{
|
||||
{ "vertex", BUFFERTYPE_VERTEX },
|
||||
{ "index", BUFFERTYPE_INDEX },
|
||||
{ "texel", BUFFERTYPE_TEXEL },
|
||||
{ "vertex", BUFFERTYPE_VERTEX },
|
||||
{ "index", BUFFERTYPE_INDEX },
|
||||
{ "texel", BUFFERTYPE_TEXEL },
|
||||
{ "shaderstorage", BUFFERTYPE_SHADER_STORAGE },
|
||||
}
|
||||
STRINGMAP_END(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName)
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ enum BufferType
|
||||
BUFFERTYPE_INDEX,
|
||||
BUFFERTYPE_UNIFORM,
|
||||
BUFFERTYPE_TEXEL,
|
||||
BUFFERTYPE_SHADER_STORAGE,
|
||||
BUFFERTYPE_MAX_ENUM
|
||||
};
|
||||
|
||||
|
||||
@@ -278,8 +278,6 @@ int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shad
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Texture *tex = luax_checktexture(L, startidx + i);
|
||||
if (tex->getTextureType() != info->textureType)
|
||||
return luaL_argerror(L, startidx + i, "invalid texture type for uniform");
|
||||
textures.push_back(tex);
|
||||
}
|
||||
|
||||
@@ -321,6 +319,7 @@ static int w_Shader_sendLuaValues(lua_State *L, int startidx, Shader *shader, co
|
||||
case Shader::UNIFORM_SAMPLER:
|
||||
return w_Shader_sendTextures(L, startidx, shader, info);
|
||||
case Shader::UNIFORM_TEXELBUFFER:
|
||||
case Shader::UNIFORM_STORAGEBUFFER:
|
||||
return w_Shader_sendBuffers(L, startidx, shader, info);
|
||||
default:
|
||||
return luaL_error(L, "Unknown variable type for shader uniform '%s", name);
|
||||
@@ -329,8 +328,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)
|
||||
return luaL_error(L, "Uniform sampler values (textures) cannot be sent to Shaders via Data objects.");
|
||||
if (info->baseType == Shader::UNIFORM_SAMPLER || 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;
|
||||
int dataidx = startidx;
|
||||
|
||||
Reference in New Issue
Block a user