mirror of
https://github.com/love2d/love.git
synced 2026-08-19 04:06:17 +02:00
Merge branch 'main' into SDL3
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -180,8 +180,9 @@ Graphics::DisplayState::DisplayState()
|
||||
defaultSamplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_LINEAR;
|
||||
}
|
||||
|
||||
Graphics::Graphics()
|
||||
: width(0)
|
||||
Graphics::Graphics(const char *name)
|
||||
: Module(M_GRAPHICS, name)
|
||||
, width(0)
|
||||
, height(0)
|
||||
, pixelWidth(0)
|
||||
, pixelHeight(0)
|
||||
@@ -1202,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;
|
||||
@@ -1218,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;
|
||||
}
|
||||
@@ -2576,21 +2589,42 @@ void Graphics::polygon(DrawMode mode, const Vector2 *coords, size_t count, bool
|
||||
|
||||
BatchedDrawCommand cmd;
|
||||
cmd.formats[0] = getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = CommonFormat::RGBAub;
|
||||
cmd.formats[1] = CommonFormat::STf_RGBAub;
|
||||
cmd.indexMode = TRIANGLEINDEX_FAN;
|
||||
cmd.vertexCount = (int)count - (skipLastFilledVertex ? 1 : 0);
|
||||
|
||||
BatchedVertexData data = requestBatchedDraw(cmd);
|
||||
|
||||
if (is2D)
|
||||
t.transformXY((Vector2 *) data.stream[0], coords, cmd.vertexCount);
|
||||
else
|
||||
t.transformXY0((Vector3 *) data.stream[0], coords, cmd.vertexCount);
|
||||
// Compute texture coordinates.
|
||||
constexpr float inf = std::numeric_limits<float>::infinity();
|
||||
Vector2 mincoord(inf, inf);
|
||||
Vector2 maxcoord(-inf, -inf);
|
||||
|
||||
for (int i = 0; i < cmd.vertexCount; i++)
|
||||
{
|
||||
Vector2 v = coords[i];
|
||||
mincoord.x = std::min(mincoord.x, v.x);
|
||||
mincoord.y = std::min(mincoord.y, v.y);
|
||||
maxcoord.x = std::max(maxcoord.x, v.x);
|
||||
maxcoord.y = std::max(maxcoord.y, v.y);
|
||||
}
|
||||
|
||||
Vector2 invsize(1.0f / (maxcoord.x - mincoord.x), 1.0f / (maxcoord.y - mincoord.y));
|
||||
Vector2 start(mincoord.x * invsize.x, mincoord.y * invsize.y);
|
||||
|
||||
Color32 c = toColor32(getColor());
|
||||
Color32 *colordata = (Color32 *) data.stream[1];
|
||||
STf_RGBAub *attributes = (STf_RGBAub *) data.stream[1];
|
||||
for (int i = 0; i < cmd.vertexCount; i++)
|
||||
colordata[i] = c;
|
||||
{
|
||||
attributes[i].s = coords[i].x * invsize.x - start.x;
|
||||
attributes[i].t = coords[i].y * invsize.y - start.y;
|
||||
attributes[i].color = c;
|
||||
}
|
||||
|
||||
if (is2D)
|
||||
t.transformXY((Vector2*)data.stream[0], coords, cmd.vertexCount);
|
||||
else
|
||||
t.transformXY0((Vector3*)data.stream[0], coords, cmd.vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2768,27 +2802,6 @@ Vector2 Graphics::inverseTransformPoint(Vector2 point)
|
||||
return p;
|
||||
}
|
||||
|
||||
void Graphics::setOrthoProjection(float w, float h, float near, float far)
|
||||
{
|
||||
if (near >= far)
|
||||
throw love::Exception("Orthographic projection Z far value must be greater than the Z near value.");
|
||||
|
||||
Matrix4 m = Matrix4::ortho(0.0f, w, 0.0f, h, near, far);
|
||||
setCustomProjection(m);
|
||||
}
|
||||
|
||||
void Graphics::setPerspectiveProjection(float verticalfov, float aspect, float near, float far)
|
||||
{
|
||||
if (near <= 0.0f)
|
||||
throw love::Exception("Perspective projection Z near value must be greater than 0.");
|
||||
|
||||
if (near >= far)
|
||||
throw love::Exception("Perspective projection Z far value must be greater than the Z near value.");
|
||||
|
||||
Matrix4 m = Matrix4::perspective(verticalfov, aspect, near, far);
|
||||
setCustomProjection(m);
|
||||
}
|
||||
|
||||
void Graphics::setCustomProjection(const Matrix4 &m)
|
||||
{
|
||||
flushBatchedDraws();
|
||||
@@ -2818,29 +2831,14 @@ void Graphics::resetProjection()
|
||||
|
||||
state.useCustomProjection = false;
|
||||
|
||||
updateDeviceProjection(Matrix4::ortho(0.0f, w, 0.0f, h, -10.0f, 10.0f));
|
||||
// NDC is y-up. The ortho() parameter names assume that as well. We want
|
||||
// a y-down projection, so we set bottom to h and top to 0.
|
||||
updateDeviceProjection(Matrix4::ortho(0.0f, w, h, 0.0f, -10.0f, 10.0f));
|
||||
}
|
||||
|
||||
void Graphics::updateDeviceProjection(const Matrix4 &projection)
|
||||
{
|
||||
// Note: graphics implementations define computeDeviceProjection.
|
||||
deviceProjectionMatrix = computeDeviceProjection(projection, isRenderTargetActive());
|
||||
}
|
||||
|
||||
Matrix4 Graphics::calculateDeviceProjection(const Matrix4 &projection, uint32 flags) const
|
||||
{
|
||||
Matrix4 m = projection;
|
||||
bool reverseZ = (flags & DEVICE_PROJECTION_REVERSE_Z) != 0;
|
||||
|
||||
if (flags & DEVICE_PROJECTION_FLIP_Y)
|
||||
m.setRow(1, -m.getRow(1));
|
||||
|
||||
if (flags & DEVICE_PROJECTION_Z_01) // Go from Z [-1, 1] to Z [0, 1].
|
||||
m.setRow(2, m.getRow(2) * (reverseZ ? -0.5f : 0.5f) + m.getRow(3));
|
||||
else if (reverseZ)
|
||||
m.setRow(2, -m.getRow(2));
|
||||
|
||||
return m;
|
||||
deviceProjectionMatrix = projection;
|
||||
}
|
||||
|
||||
STRINGMAP_CLASS_BEGIN(Graphics, Graphics::DrawMode, Graphics::DRAW_MAX_ENUM, drawMode)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -451,13 +451,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
Graphics();
|
||||
Graphics(const char *name);
|
||||
virtual ~Graphics();
|
||||
|
||||
// Implements Module.
|
||||
virtual ModuleType getModuleType() const { return M_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);
|
||||
@@ -629,6 +627,9 @@ public:
|
||||
void setMeshCullMode(CullMode cull);
|
||||
CullMode getMeshCullMode() const;
|
||||
|
||||
// Note: These are meant to be relative to the y-down default projection,
|
||||
// which may be flipped compared to device NDC. Implementations may have
|
||||
// to flip the winding internally.
|
||||
virtual void setFrontFaceWinding(Winding winding) = 0;
|
||||
Winding getFrontFaceWinding() const;
|
||||
|
||||
@@ -877,13 +878,9 @@ public:
|
||||
Vector2 transformPoint(Vector2 point);
|
||||
Vector2 inverseTransformPoint(Vector2 point);
|
||||
|
||||
void setOrthoProjection(float w, float h, float near, float far);
|
||||
void setPerspectiveProjection(float verticalfov, float aspect, float near, float far);
|
||||
void setCustomProjection(const Matrix4 &m);
|
||||
void resetProjection();
|
||||
|
||||
virtual Matrix4 computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const = 0;
|
||||
|
||||
virtual void draw(const DrawCommand &cmd) = 0;
|
||||
virtual void draw(const DrawIndexedCommand &cmd) = 0;
|
||||
virtual void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, Texture *texture) = 0;
|
||||
@@ -926,14 +923,6 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
enum DeviceProjectionFlags
|
||||
{
|
||||
DEVICE_PROJECTION_DEFAULT = 0,
|
||||
DEVICE_PROJECTION_FLIP_Y = (1 << 0),
|
||||
DEVICE_PROJECTION_Z_01 = (1 << 1),
|
||||
DEVICE_PROJECTION_REVERSE_Z = (1 << 2),
|
||||
};
|
||||
|
||||
struct DisplayState
|
||||
{
|
||||
DisplayState();
|
||||
@@ -1062,7 +1051,6 @@ protected:
|
||||
void popTransform();
|
||||
|
||||
void updateDeviceProjection(const Matrix4 &projection);
|
||||
Matrix4 calculateDeviceProjection(const Matrix4 &projection, uint32 flags) const;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -192,6 +192,7 @@ GraphicsReadback::Status GraphicsReadback::readbackBuffer(Buffer *buffer, size_t
|
||||
|
||||
if (imageData.get())
|
||||
{
|
||||
// Always lock the mutex since the user can't know when to do it.
|
||||
love::thread::Lock lock(imageData->getMutex());
|
||||
|
||||
if (imageData->getWidth() != rect.w)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -424,7 +424,7 @@ void Polyline::draw(love::graphics::Graphics *gfx)
|
||||
|
||||
Graphics::BatchedDrawCommand cmd;
|
||||
cmd.formats[0] = getSinglePositionFormat(is2D);
|
||||
cmd.formats[1] = CommonFormat::RGBAub;
|
||||
cmd.formats[1] = CommonFormat::STf_RGBAub;
|
||||
cmd.indexMode = triangle_mode;
|
||||
cmd.vertexCount = std::min(maxvertices, total_vertex_count - vertex_start);
|
||||
|
||||
@@ -435,13 +435,19 @@ void Polyline::draw(love::graphics::Graphics *gfx)
|
||||
else
|
||||
t.transformXY0((Vector3 *) data.stream[0], verts, cmd.vertexCount);
|
||||
|
||||
Color32 *colordata = (Color32 *) data.stream[1];
|
||||
STf_RGBAub *attributes = (STf_RGBAub *) data.stream[1];
|
||||
|
||||
int draw_rough_count = std::min(cmd.vertexCount, (int) vertex_count - vertex_start);
|
||||
|
||||
// Constant vertex color up to the overdraw vertices.
|
||||
// Texture coordinates are a constant value, we only have them to keep auto-batching
|
||||
// when drawing filled and line polygons together.
|
||||
for (int i = 0; i < draw_rough_count; i++)
|
||||
colordata[i] = curcolor;
|
||||
{
|
||||
attributes[i].s = 0.0f;
|
||||
attributes[i].t = 0.0f;
|
||||
attributes[i].color = curcolor;
|
||||
}
|
||||
|
||||
if (overdraw)
|
||||
{
|
||||
@@ -456,30 +462,34 @@ void Polyline::draw(love::graphics::Graphics *gfx)
|
||||
|
||||
if (draw_overdraw_count > 0)
|
||||
{
|
||||
Color32 *colors = colordata + draw_overdraw_begin;
|
||||
fill_color_array(curcolor, colors, draw_overdraw_count);
|
||||
STf_RGBAub *c = attributes + draw_overdraw_begin;
|
||||
fill_color_array(curcolor, c, draw_overdraw_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Polyline::fill_color_array(Color32 constant_color, Color32 *colors, int count)
|
||||
void Polyline::fill_color_array(Color32 constant_color, STf_RGBAub *attributes, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
Color32 c = constant_color;
|
||||
c.a *= (i+1) % 2; // avoids branching. equiv to if (i%2 == 1) c.a = 0;
|
||||
colors[i] = c;
|
||||
attributes[i].s = 0.0f;
|
||||
attributes[i].t = 0.0f;
|
||||
attributes[i].color = c;
|
||||
}
|
||||
}
|
||||
|
||||
void NoneJoinPolyline::fill_color_array(Color32 constant_color, Color32 *colors, int count)
|
||||
void NoneJoinPolyline::fill_color_array(Color32 constant_color, STf_RGBAub *attributes, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
Color32 c = constant_color;
|
||||
c.a *= (i & 3) < 2; // if (i % 4 == 2 || i % 4 == 3) c.a = 0
|
||||
colors[i] = c;
|
||||
attributes[i].s = 0.0f;
|
||||
attributes[i].t = 0.0f;
|
||||
attributes[i].color = c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -73,7 +73,7 @@ protected:
|
||||
|
||||
virtual void calc_overdraw_vertex_count(bool is_looping);
|
||||
virtual void render_overdraw(const std::vector<Vector2> &normals, float pixel_size, bool is_looping);
|
||||
virtual void fill_color_array(Color32 constant_color, Color32 *colors, int count);
|
||||
virtual void fill_color_array(Color32 constant_color, STf_RGBAub *attributes, int count);
|
||||
|
||||
/** Calculate line boundary points.
|
||||
*
|
||||
@@ -133,7 +133,7 @@ protected:
|
||||
|
||||
void calc_overdraw_vertex_count(bool is_looping) override;
|
||||
void render_overdraw(const std::vector<Vector2> &normals, float pixel_size, bool is_looping) override;
|
||||
void fill_color_array(Color32 constant_color, Color32 *colors, int count) override;
|
||||
void fill_color_array(Color32 constant_color, STf_RGBAub *attributes, int count) override;
|
||||
void renderEdge(std::vector<Vector2> &anchors, std::vector<Vector2> &normals,
|
||||
Vector2 &s, float &len_s, Vector2 &ns, const Vector2 &q,
|
||||
const Vector2 &r, float hw) override;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
+305
-81
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
// glslang
|
||||
#include "libraries/glslang/glslang/Public/ShaderLang.h"
|
||||
#include "libraries/glslang/glslang/Public/ResourceLimits.h"
|
||||
|
||||
// Needed for reflection information.
|
||||
#include "libraries/glslang/glslang/Include/Types.h"
|
||||
@@ -91,10 +92,10 @@ static const char render_uniforms[] = R"(
|
||||
// but we can't guarantee that highp is always supported in fragment shaders...
|
||||
// We *really* don't want to use mediump for these in vertex shaders though.
|
||||
#ifdef LOVE_SPLIT_UNIFORMS_PER_DRAW
|
||||
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[12];
|
||||
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
|
||||
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw2[1];
|
||||
#else
|
||||
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
|
||||
uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[14];
|
||||
#endif
|
||||
|
||||
// Older GLSL doesn't support preprocessor line continuations...
|
||||
@@ -106,12 +107,15 @@ uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
|
||||
|
||||
#define CurrentDPIScale (love_UniformsPerDraw[8].w)
|
||||
#define ConstantPointSize (love_UniformsPerDraw[9].w)
|
||||
#define ConstantColor (love_UniformsPerDraw[11])
|
||||
|
||||
#define love_ClipSpaceParams (love_UniformsPerDraw[11])
|
||||
|
||||
#define ConstantColor (love_UniformsPerDraw[12])
|
||||
|
||||
#ifdef LOVE_SPLIT_UNIFORMS_PER_DRAW
|
||||
#define love_ScreenSize (love_UniformsPerDraw2[0])
|
||||
#else
|
||||
#define love_ScreenSize (love_UniformsPerDraw[12])
|
||||
#define love_ScreenSize (love_UniformsPerDraw[13])
|
||||
#endif
|
||||
|
||||
// Alternate names
|
||||
@@ -123,16 +127,17 @@ uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
|
||||
|
||||
static const char global_functions[] = R"(
|
||||
#ifdef GL_ES
|
||||
precision mediump sampler2D;
|
||||
#if __VERSION__ >= 300 || defined(LOVE_EXT_TEXTURE_ARRAY_ENABLED)
|
||||
precision lowp sampler2DArray;
|
||||
precision mediump sampler2DArray;
|
||||
#endif
|
||||
#if __VERSION__ >= 300 || defined(GL_OES_texture_3D)
|
||||
precision lowp sampler3D;
|
||||
precision mediump sampler3D;
|
||||
#endif
|
||||
#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3)
|
||||
precision lowp sampler2DShadow;
|
||||
precision lowp samplerCubeShadow;
|
||||
precision lowp sampler2DArrayShadow;
|
||||
precision mediump sampler2DShadow;
|
||||
precision mediump samplerCubeShadow;
|
||||
precision mediump sampler2DArrayShadow;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -246,7 +251,13 @@ static const char vertex_header[] = R"(
|
||||
#endif
|
||||
)";
|
||||
|
||||
static const char vertex_functions[] = R"()";
|
||||
static const char vertex_functions[] = R"(
|
||||
vec4 love_clipSpaceTransform(vec4 clipPosition) {
|
||||
clipPosition.y *= love_ClipSpaceParams.x;
|
||||
clipPosition.z = (love_ClipSpaceParams.y * clipPosition.z + love_ClipSpaceParams.z * clipPosition.w) * love_ClipSpaceParams.w;
|
||||
return clipPosition;
|
||||
}
|
||||
)";
|
||||
|
||||
static const char vertex_main[] = R"(
|
||||
LOVE_IO_LOCATION(0) attribute vec4 VertexPosition;
|
||||
@@ -262,6 +273,7 @@ void main() {
|
||||
VaryingTexCoord = VertexTexCoord;
|
||||
VaryingColor = gammaCorrectColor(VertexColor) * ConstantColor;
|
||||
love_Position = position(ClipSpaceFromLocal, VertexPosition);
|
||||
love_Position = love_clipSpaceTransform(love_Position);
|
||||
}
|
||||
)";
|
||||
|
||||
@@ -270,6 +282,7 @@ void vertexmain();
|
||||
|
||||
void main() {
|
||||
vertexmain();
|
||||
love_Position = love_clipSpaceTransform(love_Position);
|
||||
}
|
||||
)";
|
||||
|
||||
@@ -586,7 +599,7 @@ static Shader::EntryPoint getComputeEntryPoint(const std::string &src, const std
|
||||
|
||||
} // glsl
|
||||
|
||||
static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes.");
|
||||
static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 14, "Update the array in wrap_GraphicsShader.lua if this changes.");
|
||||
|
||||
love::Type Shader::type("Shader", &Object::type);
|
||||
|
||||
@@ -691,9 +704,48 @@ Shader::Shader(StrongRef<ShaderStage> _stages[], const CompileOptions &options)
|
||||
, debugName(options.debugName)
|
||||
{
|
||||
std::string err;
|
||||
if (!validateInternal(_stages, err, validationReflection))
|
||||
if (!validateInternal(_stages, err, reflection))
|
||||
throw love::Exception("%s", err.c_str());
|
||||
|
||||
activeTextures.resize(reflection.textureCount);
|
||||
activeBuffers.resize(reflection.bufferCount);
|
||||
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
|
||||
// Default bindings for read-only resources.
|
||||
for (const auto &kvp : reflection.allUniforms)
|
||||
{
|
||||
const auto &u = *kvp.second;
|
||||
|
||||
if (u.resourceIndex < 0)
|
||||
continue;
|
||||
|
||||
if ((u.access & ACCESS_WRITE) != 0)
|
||||
continue;
|
||||
|
||||
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
auto tex = gfx->getDefaultTexture(u.textureType, u.dataBaseType);
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
tex->retain();
|
||||
activeTextures[u.resourceIndex + i] = tex;
|
||||
}
|
||||
}
|
||||
else if (u.baseType == UNIFORM_TEXELBUFFER || u.baseType == UNIFORM_STORAGEBUFFER)
|
||||
{
|
||||
auto buffer = u.baseType == UNIFORM_TEXELBUFFER
|
||||
? gfx->getDefaultTexelBuffer(u.dataBaseType)
|
||||
: gfx->getDefaultStorageBuffer();
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
buffer->retain();
|
||||
activeBuffers[u.resourceIndex + i] = buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
|
||||
stages[i] = _stages[i];
|
||||
}
|
||||
@@ -708,6 +760,18 @@ Shader::~Shader()
|
||||
|
||||
if (current == this)
|
||||
attachDefault(STANDARD_DEFAULT);
|
||||
|
||||
for (Texture *tex : activeTextures)
|
||||
{
|
||||
if (tex)
|
||||
tex->release();
|
||||
}
|
||||
|
||||
for (Buffer *buffer : activeBuffers)
|
||||
{
|
||||
if (buffer)
|
||||
buffer->release();
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::hasStage(ShaderStageType stage)
|
||||
@@ -740,6 +804,40 @@ bool Shader::isDefaultActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector4 Shader::computeClipSpaceParams(uint32 clipSpaceTransformFlags)
|
||||
{
|
||||
// See the love_clipSpaceTransform vertex shader function.
|
||||
Vector4 params(1.0f, 1.0f, 0.0f, 1.0f);
|
||||
|
||||
if (clipSpaceTransformFlags & CLIP_TRANSFORM_FLIP_Y)
|
||||
params.x = -1.0f;
|
||||
|
||||
if (clipSpaceTransformFlags & CLIP_TRANSFORM_Z_NEG1_1_TO_0_1)
|
||||
{
|
||||
params.z = 1.0f;
|
||||
params.w = 0.5f;
|
||||
}
|
||||
else if (clipSpaceTransformFlags & CLIP_TRANSFORM_Z_0_1_TO_NEG1_1)
|
||||
{
|
||||
params.y = 2.0f;
|
||||
params.z = -1.0f;
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
|
||||
{
|
||||
const auto it = reflection.allUniforms.find(name);
|
||||
return it != reflection.allUniforms.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
bool Shader::hasUniform(const std::string &name) const
|
||||
{
|
||||
const auto it = reflection.allUniforms.find(name);
|
||||
return it != reflection.allUniforms.end() && it->second->active;
|
||||
}
|
||||
|
||||
const Shader::UniformInfo *Shader::getMainTextureInfo() const
|
||||
{
|
||||
return getUniformInfo(BUILTIN_TEXTURE_MAIN);
|
||||
@@ -781,9 +879,9 @@ bool Shader::isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b)
|
||||
|
||||
void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const
|
||||
{
|
||||
if ((primtype == PRIMITIVE_POINTS) != validationReflection.usesPointSize)
|
||||
if ((primtype == PRIMITIVE_POINTS) != reflection.usesPointSize)
|
||||
{
|
||||
if (validationReflection.usesPointSize)
|
||||
if (reflection.usesPointSize)
|
||||
throw love::Exception("The active shader can only be used to draw points.");
|
||||
else
|
||||
throw love::Exception("The gl_PointSize variable must be set in a vertex shader when drawing points.");
|
||||
@@ -825,17 +923,29 @@ void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const
|
||||
|
||||
void Shader::getLocalThreadgroupSize(int *x, int *y, int *z)
|
||||
{
|
||||
*x = validationReflection.localThreadgroupSize[0];
|
||||
*y = validationReflection.localThreadgroupSize[1];
|
||||
*z = validationReflection.localThreadgroupSize[2];
|
||||
*x = reflection.localThreadgroupSize[0];
|
||||
*y = reflection.localThreadgroupSize[1];
|
||||
*z = reflection.localThreadgroupSize[2];
|
||||
}
|
||||
|
||||
bool Shader::validate(StrongRef<ShaderStage> stages[], std::string& err)
|
||||
{
|
||||
ValidationReflection reflection;
|
||||
Reflection reflection;
|
||||
return validateInternal(stages, err, reflection);
|
||||
}
|
||||
|
||||
static DataBaseType getBaseType(glslang::TBasicType basictype)
|
||||
{
|
||||
switch (basictype)
|
||||
{
|
||||
case glslang::EbtInt: return DATA_BASETYPE_INT;
|
||||
case glslang::EbtUint: return DATA_BASETYPE_UINT;
|
||||
case glslang::EbtFloat: return DATA_BASETYPE_FLOAT;
|
||||
case glslang::EbtBool: return DATA_BASETYPE_BOOL;
|
||||
default: return DATA_BASETYPE_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
static PixelFormat getPixelFormat(glslang::TLayoutFormat format)
|
||||
{
|
||||
using namespace glslang;
|
||||
@@ -885,6 +995,30 @@ static PixelFormat getPixelFormat(glslang::TLayoutFormat format)
|
||||
}
|
||||
}
|
||||
|
||||
static TextureType getTextureType(const glslang::TSampler &sampler)
|
||||
{
|
||||
if (sampler.is2D())
|
||||
return sampler.isArrayed() ? TEXTURE_2D_ARRAY : TEXTURE_2D;
|
||||
else if (sampler.dim == glslang::EsdCube)
|
||||
return sampler.isArrayed() ? TEXTURE_MAX_ENUM : TEXTURE_CUBE;
|
||||
else if (sampler.dim == glslang::Esd3D)
|
||||
return TEXTURE_VOLUME;
|
||||
else
|
||||
return TEXTURE_MAX_ENUM;
|
||||
}
|
||||
|
||||
static uint32 getStageMask(EShLanguageMask mask)
|
||||
{
|
||||
uint32 m = 0;
|
||||
if (mask & EShLangVertexMask)
|
||||
m |= SHADERSTAGEMASK_VERTEX;
|
||||
if (mask & EShLangFragmentMask)
|
||||
m |= SHADERSTAGEMASK_PIXEL;
|
||||
if (mask & EShLangComputeMask)
|
||||
m |= SHADERSTAGEMASK_COMPUTE;
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T convertData(const glslang::TConstUnion &data)
|
||||
{
|
||||
@@ -903,7 +1037,7 @@ static T convertData(const glslang::TConstUnion &data)
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err, ValidationReflection &reflection)
|
||||
bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err, Reflection &reflection)
|
||||
{
|
||||
glslang::TProgram program;
|
||||
|
||||
@@ -946,6 +1080,9 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
}
|
||||
}
|
||||
|
||||
reflection.textureCount = 0;
|
||||
reflection.bufferCount = 0;
|
||||
|
||||
for (int i = 0; i < program.getNumUniformVariables(); i++)
|
||||
{
|
||||
const glslang::TObjectReflection &info = program.getUniform(i);
|
||||
@@ -955,9 +1092,40 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
|
||||
const glslang::TQualifier &qualifiers = type->getQualifier();
|
||||
|
||||
if (type->isImage())
|
||||
UniformInfo u = {};
|
||||
|
||||
u.name = canonicaliizeUniformName(info.name);
|
||||
u.location = -1;
|
||||
u.access = ACCESS_READ;
|
||||
u.stageMask = getStageMask(info.stages);
|
||||
u.components = 1;
|
||||
u.resourceIndex = -1;
|
||||
|
||||
if (type->isSizedArray())
|
||||
u.count = type->getArraySizes()->getCumulativeSize();
|
||||
else
|
||||
u.count = 1;
|
||||
|
||||
const auto &sampler = type->getSampler();
|
||||
|
||||
if (type->isTexture() && type->getSampler().isCombined() && !sampler.isBuffer())
|
||||
{
|
||||
if ((info.stages & EShLangComputeMask) == 0)
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = getBaseType(sampler.getBasicType());
|
||||
u.isDepthSampler = sampler.isShadow();
|
||||
u.textureType = getTextureType(sampler);
|
||||
|
||||
if (u.textureType == TEXTURE_MAX_ENUM)
|
||||
continue;
|
||||
|
||||
u.resourceIndex = reflection.textureCount;
|
||||
reflection.textureCount += u.count;
|
||||
|
||||
reflection.sampledTextures[u.name] = u;
|
||||
}
|
||||
else if (type->isImage())
|
||||
{
|
||||
if ((info.stages & (~EShLangComputeMask)) != 0)
|
||||
{
|
||||
err = "Shader validation error:\nStorage Texture uniform variables (image2D, etc) are only allowed in compute shaders.";
|
||||
return false;
|
||||
@@ -965,36 +1133,63 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
|
||||
if (!qualifiers.hasFormat())
|
||||
{
|
||||
err = "Shader validation error:\nStorage Texture '" + info.name + "' must have an explicit format set in its layout declaration.";
|
||||
err = "Shader validation error:\nStorage Texture '" + u.name + "' must have an explicit format set in its layout declaration.";
|
||||
return false;
|
||||
}
|
||||
|
||||
StorageTextureReflection texreflection = {};
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.storageTextureFormat = getPixelFormat(qualifiers.getFormat());
|
||||
u.dataBaseType = getDataBaseType(u.storageTextureFormat);
|
||||
u.textureType = getTextureType(sampler);
|
||||
|
||||
texreflection.format = getPixelFormat(qualifiers.getFormat());
|
||||
if (u.textureType == TEXTURE_MAX_ENUM)
|
||||
continue;
|
||||
|
||||
u.resourceIndex = reflection.textureCount;
|
||||
reflection.textureCount += u.count;
|
||||
|
||||
if (qualifiers.isReadOnly())
|
||||
texreflection.access = ACCESS_READ;
|
||||
u.access = ACCESS_READ;
|
||||
else if (qualifiers.isWriteOnly())
|
||||
texreflection.access = ACCESS_WRITE;
|
||||
u.access = ACCESS_WRITE;
|
||||
else
|
||||
texreflection.access = (Access)(ACCESS_READ | ACCESS_WRITE);
|
||||
u.access = (Access)(ACCESS_READ | ACCESS_WRITE);
|
||||
|
||||
reflection.storageTextures[info.name] = texreflection;
|
||||
reflection.storageTextures[u.name] = u;
|
||||
}
|
||||
else if (type->getBasicType() == glslang::EbtSampler && sampler.isBuffer())
|
||||
{
|
||||
u.baseType = UNIFORM_TEXELBUFFER;
|
||||
u.dataBaseType = getBaseType(sampler.getBasicType());
|
||||
|
||||
u.resourceIndex = reflection.bufferCount;
|
||||
reflection.bufferCount += u.count;
|
||||
|
||||
reflection.texelBuffers[u.name] = u;
|
||||
}
|
||||
else if (!type->isOpaque())
|
||||
{
|
||||
LocalUniform u = {};
|
||||
auto &values = u.initializerValues;
|
||||
std::vector<LocalUniformValue> values;
|
||||
const glslang::TConstUnionArray *constarray = info.getConstArray();
|
||||
|
||||
if (type->isMatrix())
|
||||
{
|
||||
u.matrix.rows = type->getMatrixRows();
|
||||
u.matrix.columns = type->getMatrixCols();
|
||||
}
|
||||
else
|
||||
{
|
||||
u.components = type->getVectorSize();
|
||||
}
|
||||
|
||||
// Store initializer values for local uniforms. Some love graphics
|
||||
// backends strip these out of the shader so we need to be able to
|
||||
// access them (to re-send them) by getting them here.
|
||||
switch (type->getBasicType())
|
||||
{
|
||||
case glslang::EbtFloat:
|
||||
u.dataType = DATA_BASETYPE_FLOAT;
|
||||
u.baseType = type->isMatrix() ? UNIFORM_MATRIX : UNIFORM_FLOAT;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
if (constarray != nullptr)
|
||||
{
|
||||
values.resize(constarray->size());
|
||||
@@ -1003,7 +1198,8 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
}
|
||||
break;
|
||||
case glslang::EbtUint:
|
||||
u.dataType = DATA_BASETYPE_UINT;
|
||||
u.baseType = UNIFORM_UINT;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
if (constarray != nullptr)
|
||||
{
|
||||
values.resize(constarray->size());
|
||||
@@ -1012,7 +1208,8 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
}
|
||||
break;
|
||||
case glslang::EbtBool:
|
||||
u.dataType = DATA_BASETYPE_BOOL;
|
||||
u.baseType = UNIFORM_BOOL;
|
||||
u.dataBaseType = DATA_BASETYPE_BOOL;
|
||||
if (constarray != nullptr)
|
||||
{
|
||||
values.resize(constarray->size());
|
||||
@@ -1022,7 +1219,8 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
break;
|
||||
case glslang::EbtInt:
|
||||
default:
|
||||
u.dataType = DATA_BASETYPE_INT;
|
||||
u.baseType = UNIFORM_INT;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
if (constarray != nullptr)
|
||||
{
|
||||
values.resize(constarray->size());
|
||||
@@ -1032,7 +1230,8 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
break;
|
||||
}
|
||||
|
||||
reflection.localUniforms[info.name] = u;
|
||||
reflection.localUniforms[u.name] = u;
|
||||
reflection.localUniformInitializerValues[u.name] = values;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1044,7 +1243,7 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
{
|
||||
const glslang::TQualifier &qualifiers = type->getQualifier();
|
||||
|
||||
if ((!qualifiers.isReadOnly() || qualifiers.isWriteOnly()) && (info.stages & EShLangComputeMask) == 0)
|
||||
if ((!qualifiers.isReadOnly() || qualifiers.isWriteOnly()) && ((info.stages & (~EShLangComputeMask)) != 0))
|
||||
{
|
||||
err = "Shader validation error:\nStorage Buffer block '" + info.name + "' must be marked as readonly in vertex and pixel shaders.";
|
||||
return false;
|
||||
@@ -1070,18 +1269,32 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
return false;
|
||||
}
|
||||
|
||||
BufferReflection bufferReflection = {};
|
||||
bufferReflection.stride = (size_t) info.size;
|
||||
bufferReflection.memberCount = (size_t) info.numMembers;
|
||||
UniformInfo u = {};
|
||||
u.name = canonicaliizeUniformName(info.name);
|
||||
u.location = -1;
|
||||
u.stageMask = getStageMask(info.stages);
|
||||
u.components = 1;
|
||||
u.baseType = UNIFORM_STORAGEBUFFER;
|
||||
|
||||
if (type->isSizedArray())
|
||||
u.count = type->getArraySizes()->getCumulativeSize();
|
||||
else
|
||||
u.count = 1;
|
||||
|
||||
u.bufferStride = (size_t) info.size;
|
||||
u.bufferMemberCount = (size_t) info.numMembers;
|
||||
|
||||
u.resourceIndex = reflection.bufferCount;
|
||||
reflection.bufferCount += u.count;
|
||||
|
||||
if (qualifiers.isReadOnly())
|
||||
bufferReflection.access = ACCESS_READ;
|
||||
u.access = ACCESS_READ;
|
||||
else if (qualifiers.isWriteOnly())
|
||||
bufferReflection.access = ACCESS_WRITE;
|
||||
u.access = ACCESS_WRITE;
|
||||
else
|
||||
bufferReflection.access = (Access)(ACCESS_READ | ACCESS_WRITE);
|
||||
u.access = (Access)(ACCESS_READ | ACCESS_WRITE);
|
||||
|
||||
reflection.storageBuffers[info.name] = bufferReflection;
|
||||
reflection.storageBuffers[u.name] = u;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1090,6 +1303,21 @@ bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err,
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &kvp : reflection.texelBuffers)
|
||||
reflection.allUniforms[kvp.first] = &kvp.second;
|
||||
|
||||
for (auto &kvp : reflection.storageBuffers)
|
||||
reflection.allUniforms[kvp.first] = &kvp.second;
|
||||
|
||||
for (auto &kvp : reflection.sampledTextures)
|
||||
reflection.allUniforms[kvp.first] = &kvp.second;
|
||||
|
||||
for (auto &kvp : reflection.storageTextures)
|
||||
reflection.allUniforms[kvp.first] = &kvp.second;
|
||||
|
||||
for (auto &kvp : reflection.localUniforms)
|
||||
reflection.allUniforms[kvp.first] = &kvp.second;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1141,7 +1369,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;
|
||||
@@ -1216,41 +1444,6 @@ bool Shader::validateBuffer(const UniformInfo *info, Buffer *buffer, bool intern
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Shader::fillUniformReflectionData(UniformInfo &u)
|
||||
{
|
||||
const auto &r = validationReflection;
|
||||
|
||||
if (u.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
const auto reflectionit = r.storageTextures.find(u.name);
|
||||
if (reflectionit != r.storageTextures.end())
|
||||
{
|
||||
u.storageTextureFormat = reflectionit->second.format;
|
||||
u.access = reflectionit->second.access;
|
||||
return true;
|
||||
}
|
||||
|
||||
// No reflection info - maybe glslang was better at detecting dead code
|
||||
// than the driver's compiler?
|
||||
return false;
|
||||
}
|
||||
else if (u.baseType == UNIFORM_STORAGEBUFFER)
|
||||
{
|
||||
const auto reflectionit = r.storageBuffers.find(u.name);
|
||||
if (reflectionit != r.storageBuffers.end())
|
||||
{
|
||||
u.bufferStride = reflectionit->second.stride;
|
||||
u.bufferMemberCount = reflectionit->second.memberCount;
|
||||
u.access = reflectionit->second.access;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Shader::getShaderStageDebugName(ShaderStageType stage) const
|
||||
{
|
||||
std::string name = debugName;
|
||||
@@ -1265,9 +1458,40 @@ std::string Shader::getShaderStageDebugName(ShaderStageType stage) const
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string Shader::canonicaliizeUniformName(const std::string &n)
|
||||
{
|
||||
std::string name(n);
|
||||
|
||||
// Some drivers/compilers append "[0]" to the end of array uniform names.
|
||||
if (name.length() > 3)
|
||||
{
|
||||
size_t findpos = name.rfind("[0]");
|
||||
if (findpos != std::string::npos && findpos == name.length() - 3)
|
||||
name.erase(name.length() - 3);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
void Shader::handleUnknownUniformName(const char */*name*/)
|
||||
{
|
||||
// TODO: do something here?
|
||||
}
|
||||
|
||||
bool Shader::initialize()
|
||||
{
|
||||
return glslang::InitializeProcess();
|
||||
bool success = glslang::InitializeProcess();
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
TBuiltInResource *resources = GetResources();
|
||||
*resources = *GetDefaultResources();
|
||||
|
||||
// This is 32 in the default resource struct, which is too high for Metal.
|
||||
// TODO: Set this based on what the system actually supports?
|
||||
resources->maxDrawBuffers = 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shader::deinitialize()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -108,6 +108,14 @@ public:
|
||||
ACCESS_WRITE = (1 << 1),
|
||||
};
|
||||
|
||||
enum ClipSpaceTransformFlags
|
||||
{
|
||||
CLIP_TRANSFORM_NONE = 0,
|
||||
CLIP_TRANSFORM_FLIP_Y = 1 << 0,
|
||||
CLIP_TRANSFORM_Z_NEG1_1_TO_0_1 = 1 << 1,
|
||||
CLIP_TRANSFORM_Z_0_1_TO_NEG1_1 = 1 << 2,
|
||||
};
|
||||
|
||||
struct CompileOptions
|
||||
{
|
||||
std::map<std::string, std::string> defines;
|
||||
@@ -129,6 +137,10 @@ public:
|
||||
|
||||
struct UniformInfo
|
||||
{
|
||||
UniformType baseType;
|
||||
uint32 stageMask;
|
||||
bool active;
|
||||
|
||||
int location;
|
||||
int count;
|
||||
|
||||
@@ -138,7 +150,6 @@ public:
|
||||
MatrixSize matrix;
|
||||
};
|
||||
|
||||
UniformType baseType;
|
||||
DataBaseType dataBaseType;
|
||||
TextureType textureType;
|
||||
Access access;
|
||||
@@ -148,6 +159,8 @@ public:
|
||||
size_t bufferMemberCount;
|
||||
std::string name;
|
||||
|
||||
int resourceIndex;
|
||||
|
||||
union
|
||||
{
|
||||
void *data;
|
||||
@@ -157,12 +170,6 @@ public:
|
||||
};
|
||||
|
||||
size_t dataSize;
|
||||
|
||||
union
|
||||
{
|
||||
Texture **textures;
|
||||
Buffer **buffers;
|
||||
};
|
||||
};
|
||||
|
||||
union LocalUniformValue
|
||||
@@ -178,6 +185,7 @@ public:
|
||||
Matrix4 transformMatrix;
|
||||
Matrix4 projectionMatrix;
|
||||
Vector4 normalMatrix[3]; // 3x3 matrix padded to an array of 3 vector4s.
|
||||
Vector4 clipSpaceParams;
|
||||
Colorf constantColor;
|
||||
|
||||
// Pixel shader-centric variables past this point.
|
||||
@@ -213,6 +221,18 @@ public:
|
||||
**/
|
||||
static bool isDefaultActive();
|
||||
|
||||
/**
|
||||
* Used for transforming standardized post-projection clip space positions
|
||||
* into the backend's current clip space.
|
||||
* Right now, the standard is:
|
||||
* NDC y is [-1, 1] starting at the bottom (y-up).
|
||||
* NDC z is [-1, 1].
|
||||
* Pixel coordinates are y-down.
|
||||
* Pixel (0, 0) in a texture is the top-left.
|
||||
* Aside from NDC z, this matches Metal and D3D12.
|
||||
*/
|
||||
static Vector4 computeClipSpaceParams(uint32 clipSpaceTransformFlags);
|
||||
|
||||
/**
|
||||
* Returns any warnings this Shader may have generated.
|
||||
**/
|
||||
@@ -222,7 +242,7 @@ public:
|
||||
|
||||
virtual int getVertexAttributeIndex(const std::string &name) = 0;
|
||||
|
||||
virtual const UniformInfo *getUniformInfo(const std::string &name) const = 0;
|
||||
const UniformInfo *getUniformInfo(const std::string &name) const;
|
||||
virtual const UniformInfo *getUniformInfo(BuiltinUniform builtin) const = 0;
|
||||
|
||||
virtual void updateUniform(const UniformInfo *info, int count) = 0;
|
||||
@@ -234,7 +254,7 @@ public:
|
||||
* Gets whether a uniform with the specified name exists and is actively
|
||||
* used in the shader.
|
||||
**/
|
||||
virtual bool hasUniform(const std::string &name) const = 0;
|
||||
bool hasUniform(const std::string &name) const;
|
||||
|
||||
/**
|
||||
* Sets the textures used when rendering a video. For internal use only.
|
||||
@@ -264,39 +284,31 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
struct BufferReflection
|
||||
struct Reflection
|
||||
{
|
||||
size_t stride;
|
||||
size_t memberCount;
|
||||
Access access;
|
||||
};
|
||||
std::map<std::string, UniformInfo> texelBuffers;
|
||||
std::map<std::string, UniformInfo> storageBuffers;
|
||||
std::map<std::string, UniformInfo> sampledTextures;
|
||||
std::map<std::string, UniformInfo> storageTextures;
|
||||
std::map<std::string, UniformInfo> localUniforms;
|
||||
|
||||
struct StorageTextureReflection
|
||||
{
|
||||
PixelFormat format;
|
||||
Access access;
|
||||
};
|
||||
std::map<std::string, UniformInfo *> allUniforms;
|
||||
|
||||
struct LocalUniform
|
||||
{
|
||||
DataBaseType dataType;
|
||||
std::vector<LocalUniformValue> initializerValues;
|
||||
};
|
||||
std::map<std::string, std::vector<LocalUniformValue>> localUniformInitializerValues;
|
||||
|
||||
int textureCount;
|
||||
int bufferCount;
|
||||
|
||||
struct ValidationReflection
|
||||
{
|
||||
std::map<std::string, BufferReflection> storageBuffers;
|
||||
std::map<std::string, StorageTextureReflection> storageTextures;
|
||||
std::map<std::string, LocalUniform> localUniforms;
|
||||
int localThreadgroupSize[3];
|
||||
bool usesPointSize;
|
||||
};
|
||||
|
||||
bool fillUniformReflectionData(UniformInfo &u);
|
||||
|
||||
std::string getShaderStageDebugName(ShaderStageType stage) const;
|
||||
|
||||
static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, ValidationReflection &reflection);
|
||||
void handleUnknownUniformName(const char *name);
|
||||
|
||||
static std::string canonicaliizeUniformName(const std::string &name);
|
||||
static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, Reflection &reflection);
|
||||
static DataBaseType getDataBaseType(PixelFormat format);
|
||||
static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b);
|
||||
|
||||
@@ -305,7 +317,10 @@ protected:
|
||||
|
||||
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM];
|
||||
|
||||
ValidationReflection validationReflection;
|
||||
Reflection reflection;
|
||||
|
||||
std::vector<Texture *> activeTextures;
|
||||
std::vector<Buffer *> activeBuffers;
|
||||
|
||||
std::string debugName;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -62,7 +62,7 @@ ShaderStage::ShaderStage(Graphics *gfx, ShaderStageType stage, const std::string
|
||||
|
||||
bool forwardcompat = supportsGLSL3 && !forcedefault;
|
||||
|
||||
if (!glslangShader->parse(GetDefaultResources(), defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
|
||||
if (!glslangShader->parse(GetResources(), defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
|
||||
{
|
||||
const char *stagename = "unknown";
|
||||
getConstant(stage, stagename);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -48,6 +48,14 @@ enum ShaderStageType
|
||||
SHADERSTAGE_MAX_ENUM
|
||||
};
|
||||
|
||||
enum ShaderStageMask
|
||||
{
|
||||
SHADERSTAGEMASK_NONE = 0,
|
||||
SHADERSTAGEMASK_VERTEX = 1 << SHADERSTAGE_VERTEX,
|
||||
SHADERSTAGEMASK_PIXEL = 1 << SHADERSTAGE_PIXEL,
|
||||
SHADERSTAGEMASK_COMPUTE = 1 << SHADERSTAGE_COMPUTE,
|
||||
};
|
||||
|
||||
class ShaderStage : public love::Object
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
BufferUsage getMode() const { return mode; }
|
||||
size_t getUsableSize() const { return bufferSize - frameGPUReadOffset; }
|
||||
|
||||
virtual size_t getGPUReadOffset() const = 0;
|
||||
|
||||
virtual MapInfo map(size_t minsize) = 0;
|
||||
virtual size_t unmap(size_t usedsize) = 0;
|
||||
virtual void markUsed(size_t usedsize) = 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -167,6 +167,7 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices)
|
||||
, renderTarget(settings.renderTarget)
|
||||
, computeWrite(settings.computeWrite)
|
||||
, readable(true)
|
||||
, viewFormats(settings.viewFormats)
|
||||
, mipmapsMode(settings.mipmaps)
|
||||
, width(settings.width)
|
||||
, height(settings.height)
|
||||
@@ -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,32 +287,30 @@ 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))
|
||||
for (PixelFormat viewformat : viewFormats)
|
||||
{
|
||||
const char *fstr = "unknown";
|
||||
love::getConstant(format, fstr);
|
||||
if (getLinearPixelFormat(viewformat) == getLinearPixelFormat(format))
|
||||
continue;
|
||||
|
||||
const char *readablestr = "";
|
||||
if (readable != !isPixelFormatDepthStencil(format))
|
||||
readablestr = readable ? " readable" : " non-readable";
|
||||
if (isPixelFormatCompressed(format) || isPixelFormatCompressed(viewformat))
|
||||
throw love::Exception("Compressed textures cannot use different pixel formats for texture views, aside from sRGB versus linear variants of the same pixel format.");
|
||||
|
||||
const char *rtstr = "";
|
||||
if (computeWrite)
|
||||
rtstr = " as a compute shader-writable texture";
|
||||
else if (renderTarget)
|
||||
rtstr = " as a render target";
|
||||
if (isPixelFormatColor(viewformat) != isPixelFormatColor(format))
|
||||
throw love::Exception("Color-format textures cannot use depth/stencil pixel formats and vice versa, in texture views.");
|
||||
|
||||
throw love::Exception("The %s%s pixel format is not supported%s on this system.", fstr, readablestr, rtstr);
|
||||
// TODO: depth[24|32f]_stencil8 -> stencil8 can work.
|
||||
if (isPixelFormatDepthStencil(viewformat))
|
||||
throw love::Exception("Using different pixel formats for texture views is not currently supported for depth or stencil formats.");
|
||||
|
||||
size_t viewbytes = getPixelFormatBlockSize(viewformat);
|
||||
size_t basebytes = getPixelFormatBlockSize(format);
|
||||
|
||||
if (viewbytes != basebytes)
|
||||
throw love::Exception("Texture view pixel formats must have the same bits per pixel as the base texture's pixel format.");
|
||||
}
|
||||
|
||||
validatePixelFormat(gfx);
|
||||
|
||||
if (!caps.textureTypes[texType])
|
||||
{
|
||||
const char *textypestr = "unknown";
|
||||
@@ -330,10 +331,131 @@ 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)
|
||||
, readable(base->readable)
|
||||
, viewFormats(base->viewFormats)
|
||||
, 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->getWidth(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.");
|
||||
}
|
||||
|
||||
if (format != base->getPixelFormat())
|
||||
{
|
||||
if (std::find(viewFormats.begin(), viewFormats.end(), format) == viewFormats.end())
|
||||
throw love::Exception("Using a different pixel format in a texture view requires the original texture to be created with a 'viewformats' setting that includes the given format in its list.");
|
||||
}
|
||||
|
||||
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 +474,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();
|
||||
|
||||
@@ -451,14 +573,8 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
|
||||
|
||||
void Texture::uploadImageData(love::image::ImageDataBase *d, int level, int slice, int x, int y)
|
||||
{
|
||||
love::image::ImageData *id = dynamic_cast<love::image::ImageData *>(d);
|
||||
|
||||
love::thread::EmptyLock lock;
|
||||
if (id != nullptr)
|
||||
lock.setLock(id->getMutex());
|
||||
|
||||
Rect rect = {x, y, d->getWidth(), d->getHeight()};
|
||||
uploadByteData(d->getFormat(), d->getData(), d->getSize(), level, slice, rect);
|
||||
uploadByteData(d->getData(), d->getSize(), level, slice, rect);
|
||||
}
|
||||
|
||||
void Texture::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, int x, int y, bool reloadmipmaps)
|
||||
@@ -477,7 +593,9 @@ void Texture::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap
|
||||
if (getHandle() == 0)
|
||||
return;
|
||||
|
||||
if (d->getFormat() != getPixelFormat())
|
||||
// ImageData format might be linear but intended to be used as sRGB, so we
|
||||
// don't error if only the sRGBness is different.
|
||||
if (getLinearPixelFormat(d->getFormat()) != getLinearPixelFormat(getPixelFormat()))
|
||||
throw love::Exception("Pixel formats must match.");
|
||||
|
||||
if (mipmap < 0 || mipmap >= getMipmapCount())
|
||||
@@ -533,7 +651,7 @@ void Texture::replacePixels(const void *data, size_t size, int slice, int mipmap
|
||||
|
||||
Graphics::flushBatchedDrawsGlobal();
|
||||
|
||||
uploadByteData(format, data, size, mipmap, slice, rect);
|
||||
uploadByteData(data, size, mipmap, slice, rect);
|
||||
|
||||
if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1)
|
||||
generateMipmaps();
|
||||
@@ -586,36 +704,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);
|
||||
@@ -689,28 +777,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
|
||||
@@ -785,6 +884,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)
|
||||
{
|
||||
@@ -975,6 +1103,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 },
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
// C
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -174,6 +175,7 @@ public:
|
||||
SETTING_MSAA,
|
||||
SETTING_RENDER_TARGET,
|
||||
SETTING_COMPUTE_WRITE,
|
||||
SETTING_VIEW_FORMATS,
|
||||
SETTING_READABLE,
|
||||
SETTING_DEBUGNAME,
|
||||
SETTING_MAX_ENUM
|
||||
@@ -194,10 +196,22 @@ public:
|
||||
int msaa = 1;
|
||||
bool renderTarget = false;
|
||||
bool computeWrite = false;
|
||||
std::vector<PixelFormat> viewFormats;
|
||||
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 +242,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 +273,15 @@ 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; }
|
||||
|
||||
const std::vector<PixelFormat> &getViewFormats() const { return viewFormats; }
|
||||
|
||||
bool isCompressed() const;
|
||||
bool isFormatLinear() const;
|
||||
@@ -285,11 +305,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,15 +333,22 @@ 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);
|
||||
virtual void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) = 0;
|
||||
virtual void uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r) = 0;
|
||||
|
||||
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;
|
||||
|
||||
@@ -327,6 +357,8 @@ protected:
|
||||
bool computeWrite;
|
||||
bool readable;
|
||||
|
||||
std::vector<PixelFormat> viewFormats;
|
||||
|
||||
MipmapsMode mipmapsMode;
|
||||
|
||||
int width;
|
||||
@@ -349,6 +381,9 @@ protected:
|
||||
|
||||
std::string debugName;
|
||||
|
||||
ViewInfo rootView;
|
||||
ViewInfo parentView;
|
||||
|
||||
}; // Texture
|
||||
|
||||
} // graphics
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -60,14 +60,10 @@ public:
|
||||
Graphics();
|
||||
virtual ~Graphics();
|
||||
|
||||
// Implements Module.
|
||||
const char *getName() const override { return "love.graphics.metal"; }
|
||||
|
||||
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;
|
||||
|
||||
void backbufferChanged(int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, bool backbufferdepth, int msaa) override;
|
||||
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, bool backbufferdepth, int msaa) override;
|
||||
void unSetMode() override;
|
||||
@@ -244,6 +240,7 @@ private:
|
||||
StreamBuffer *uniformBuffer;
|
||||
StreamBuffer::MapInfo uniformBufferData;
|
||||
size_t uniformBufferOffset;
|
||||
size_t uniformBufferGPUStart;
|
||||
|
||||
Buffer *defaultAttributesBuffer;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -263,7 +263,8 @@ struct DefaultVertexAttributes
|
||||
Graphics *Graphics::graphicsInstance = nullptr;
|
||||
|
||||
Graphics::Graphics()
|
||||
: device(nil)
|
||||
: love::graphics::Graphics("love.graphics.metal")
|
||||
, device(nil)
|
||||
, commandQueue(nil)
|
||||
, commandBuffer(nil)
|
||||
, renderEncoder(nil)
|
||||
@@ -279,6 +280,7 @@ Graphics::Graphics()
|
||||
, attachmentStoreActions()
|
||||
, renderBindings()
|
||||
, uniformBufferOffset(0)
|
||||
, uniformBufferGPUStart(0)
|
||||
, defaultAttributesBuffer(nullptr)
|
||||
, families()
|
||||
{ @autoreleasepool {
|
||||
@@ -340,6 +342,7 @@ Graphics::Graphics()
|
||||
};
|
||||
|
||||
Buffer::Settings attribsettings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC);
|
||||
attribsettings.debugName = "Default Vertex Attributes";
|
||||
|
||||
defaultAttributesBuffer = newBuffer(attribsettings, dataformat, &defaults, sizeof(DefaultVertexAttributes), 0);
|
||||
}
|
||||
@@ -432,6 +435,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);
|
||||
@@ -457,12 +465,6 @@ love::graphics::GraphicsReadback *Graphics::newReadbackInternal(ReadbackMethod m
|
||||
return new GraphicsReadback(this, method, texture, slice, mipmap, rect, dest, destx, desty);
|
||||
}
|
||||
|
||||
Matrix4 Graphics::computeDeviceProjection(const Matrix4 &projection, bool /*rendertotexture*/) const
|
||||
{
|
||||
uint32 flags = DEVICE_PROJECTION_FLIP_Y;
|
||||
return calculateDeviceProjection(projection, flags);
|
||||
}
|
||||
|
||||
void Graphics::backbufferChanged(int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, bool backbufferdepth, int msaa)
|
||||
{
|
||||
bool sizechanged = width != this->width || height != this->height
|
||||
@@ -913,8 +915,8 @@ void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, const Verte
|
||||
const auto &rt = state.renderTargets.getFirstTarget();
|
||||
if (rt.texture.get())
|
||||
{
|
||||
rtw = rt.texture->getPixelWidth();
|
||||
rth = rt.texture->getPixelHeight();
|
||||
rtw = rt.texture->getPixelWidth(rt.mipmap);
|
||||
rth = rt.texture->getPixelHeight(rt.mipmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1028,14 +1030,19 @@ bool Graphics::applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::g
|
||||
if (uniformBuffer->getSize() < uniformBufferOffset + size)
|
||||
{
|
||||
size_t newsize = uniformBuffer->getSize() * 2;
|
||||
if (uniformBufferOffset > 0)
|
||||
uniformBuffer->nextFrame();
|
||||
uniformBuffer->release();
|
||||
uniformBuffer = CreateStreamBuffer(device, BUFFERUSAGE_VERTEX, newsize);
|
||||
uniformBuffer = CreateStreamBuffer(device, BUFFERUSAGE_UNIFORM, newsize);
|
||||
uniformBufferData = {};
|
||||
uniformBufferOffset = 0;
|
||||
}
|
||||
|
||||
if (uniformBufferData.data == nullptr)
|
||||
{
|
||||
uniformBufferData = uniformBuffer->map(uniformBuffer->getSize());
|
||||
uniformBufferGPUStart = uniformBuffer->getGPUReadOffset();
|
||||
}
|
||||
|
||||
memcpy(uniformBufferData.data + uniformBufferOffset, bufferdata, size);
|
||||
|
||||
@@ -1043,7 +1050,7 @@ bool Graphics::applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::g
|
||||
int uniformindex = Shader::getUniformBufferBinding();
|
||||
|
||||
auto &bindings = renderBindings;
|
||||
setBuffer(encoder, bindings, uniformindex, buffer, uniformBufferOffset);
|
||||
setBuffer(encoder, bindings, uniformindex, buffer, uniformBufferGPUStart + uniformBufferOffset);
|
||||
|
||||
uniformBufferOffset += alignUp(size, alignment);
|
||||
|
||||
@@ -1120,12 +1127,15 @@ void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, lo
|
||||
// Same with point size.
|
||||
builtins->normalMatrix[1].w = getPointSize();
|
||||
|
||||
uint32 flags = Shader::CLIP_TRANSFORM_Z_NEG1_1_TO_0_1;
|
||||
builtins->clipSpaceParams = Shader::computeClipSpaceParams(flags);
|
||||
|
||||
builtins->screenSizeParams = Vector4(getPixelWidth(), getPixelHeight(), 1.0f, 0.0f);
|
||||
auto rt = states.back().renderTargets.getFirstTarget().texture.get();
|
||||
if (rt != nullptr)
|
||||
auto rt = states.back().renderTargets.getFirstTarget();
|
||||
if (rt.texture.get())
|
||||
{
|
||||
builtins->screenSizeParams.x = rt->getPixelWidth();
|
||||
builtins->screenSizeParams.y = rt->getPixelHeight();
|
||||
builtins->screenSizeParams.x = rt.texture->getPixelWidth(rt.mipmap);
|
||||
builtins->screenSizeParams.y = rt.texture->getPixelHeight(rt.mipmap);
|
||||
}
|
||||
|
||||
builtins->constantColor = getColor();
|
||||
@@ -1134,14 +1144,19 @@ void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, lo
|
||||
if (uniformBuffer->getSize() < uniformBufferOffset + size)
|
||||
{
|
||||
size_t newsize = uniformBuffer->getSize() * 2;
|
||||
if (uniformBufferOffset > 0)
|
||||
uniformBuffer->nextFrame();
|
||||
uniformBuffer->release();
|
||||
uniformBuffer = CreateStreamBuffer(device, BUFFERUSAGE_VERTEX, newsize);
|
||||
uniformBuffer = CreateStreamBuffer(device, BUFFERUSAGE_UNIFORM, newsize);
|
||||
uniformBufferData = {};
|
||||
uniformBufferOffset = 0;
|
||||
}
|
||||
|
||||
if (uniformBufferData.data == nullptr)
|
||||
{
|
||||
uniformBufferData = uniformBuffer->map(uniformBuffer->getSize());
|
||||
uniformBufferGPUStart = uniformBuffer->getGPUReadOffset();
|
||||
}
|
||||
|
||||
memcpy(uniformBufferData.data + uniformBufferOffset, bufferdata, size);
|
||||
|
||||
@@ -1149,8 +1164,8 @@ void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, lo
|
||||
int uniformindex = Shader::getUniformBufferBinding();
|
||||
|
||||
auto &bindings = renderBindings;
|
||||
setBuffer(renderEncoder, bindings, SHADERSTAGE_VERTEX, uniformindex, buffer, uniformBufferOffset);
|
||||
setBuffer(renderEncoder, bindings, SHADERSTAGE_PIXEL, uniformindex, buffer, uniformBufferOffset);
|
||||
setBuffer(renderEncoder, bindings, SHADERSTAGE_VERTEX, uniformindex, buffer, uniformBufferGPUStart + uniformBufferOffset);
|
||||
setBuffer(renderEncoder, bindings, SHADERSTAGE_PIXEL, uniformindex, buffer, uniformBufferGPUStart + uniformBufferOffset);
|
||||
|
||||
uniformBufferOffset += alignUp(size, alignment);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -107,12 +107,10 @@ public:
|
||||
void attach() override;
|
||||
std::string getWarnings() const override { return ""; }
|
||||
int getVertexAttributeIndex(const std::string &name) override;
|
||||
const UniformInfo *getUniformInfo(const std::string &name) const override;
|
||||
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
|
||||
void updateUniform(const UniformInfo *info, int count) override;
|
||||
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override;
|
||||
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
|
||||
bool hasUniform(const std::string &name) const override;
|
||||
ptrdiff_t getHandle() const override { return 0; }
|
||||
void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;
|
||||
|
||||
@@ -140,13 +138,11 @@ private:
|
||||
};
|
||||
|
||||
void buildLocalUniforms(const spirv_cross::CompilerMSL &msl, const spirv_cross::SPIRType &type, size_t baseoffset, const std::string &basename);
|
||||
void addImage(const spirv_cross::CompilerMSL &msl, const spirv_cross::Resource &resource, UniformType baseType);
|
||||
void compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &program);
|
||||
|
||||
id<MTLFunction> functions[SHADERSTAGE_MAX_ENUM];
|
||||
|
||||
UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
|
||||
std::map<std::string, UniformInfo> uniforms;
|
||||
|
||||
uint8 *localUniformStagingData;
|
||||
uint8 *localUniformBufferData;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -208,7 +208,7 @@ Shader::Shader(id<MTLDevice> device, StrongRef<love::graphics::ShaderStage> stag
|
||||
forcedefault = true;
|
||||
#endif
|
||||
|
||||
if (!tshader->parse(GetDefaultResources(), defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
|
||||
if (!tshader->parse(GetResources(), defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
|
||||
{
|
||||
const char *stagename = "unknown";
|
||||
ShaderStage::getConstant(stage, stagename);
|
||||
@@ -299,134 +299,47 @@ void Shader::buildLocalUniforms(const spirv_cross::CompilerMSL &msl, const spirv
|
||||
continue;
|
||||
}
|
||||
|
||||
name = canonicaliizeUniformName(name);
|
||||
|
||||
if (offset + membersize > localUniformBufferSize)
|
||||
throw love::Exception("Invalid uniform offset + size for '%s' (offset=%d, size=%d, buffer size=%d)", name.c_str(), (int)offset, (int)membersize, (int)localUniformBufferSize);
|
||||
|
||||
UniformInfo u = {};
|
||||
u.name = name;
|
||||
auto uniformit = reflection.allUniforms.find(name);
|
||||
if (uniformit == reflection.allUniforms.end())
|
||||
{
|
||||
handleUnknownUniformName(name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
UniformInfo &u = *(uniformit->second);
|
||||
u.active = true;
|
||||
|
||||
if (u.dataSize > 0)
|
||||
continue;
|
||||
|
||||
u.dataSize = membersize;
|
||||
u.count = membertype.array.empty() ? 1 : membertype.array[0];
|
||||
u.components = 1;
|
||||
|
||||
u.data = localUniformStagingData + offset;
|
||||
if (membertype.columns == 1)
|
||||
{
|
||||
if (membertype.basetype == SPIRType::Int)
|
||||
u.baseType = UNIFORM_INT;
|
||||
else if (membertype.basetype == SPIRType::UInt)
|
||||
u.baseType = UNIFORM_UINT;
|
||||
else
|
||||
u.baseType = UNIFORM_FLOAT;
|
||||
u.components = membertype.vecsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
u.baseType = UNIFORM_MATRIX;
|
||||
u.matrix.rows = membertype.vecsize;
|
||||
u.matrix.columns = membertype.columns;
|
||||
}
|
||||
|
||||
const auto &reflectionit = validationReflection.localUniforms.find(u.name);
|
||||
if (reflectionit != validationReflection.localUniforms.end())
|
||||
const auto &reflectionit = reflection.localUniformInitializerValues.find(u.name);
|
||||
if (reflectionit != reflection.localUniformInitializerValues.end())
|
||||
{
|
||||
const auto &localuniform = reflectionit->second;
|
||||
const auto &values = localuniform.initializerValues;
|
||||
const auto &values = reflectionit->second;
|
||||
if (!values.empty())
|
||||
memcpy(u.data, values.data(), std::min(u.dataSize, values.size() * sizeof(LocalUniformValue)));
|
||||
}
|
||||
|
||||
uniforms[u.name] = u;
|
||||
|
||||
BuiltinUniform builtin = BUILTIN_MAX_ENUM;
|
||||
if (getConstant(u.name.c_str(), builtin))
|
||||
{
|
||||
if (builtin == BUILTIN_UNIFORMS_PER_DRAW)
|
||||
builtinUniformDataOffset = offset;
|
||||
builtinUniformInfo[builtin] = &uniforms[u.name];
|
||||
builtinUniformInfo[builtin] = &u;
|
||||
}
|
||||
|
||||
updateUniform(&u, u.count);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::addImage(const spirv_cross::CompilerMSL &msl, const spirv_cross::Resource &resource, UniformType baseType)
|
||||
{
|
||||
using namespace spirv_cross;
|
||||
|
||||
const SPIRType &basetype = msl.get_type(resource.base_type_id);
|
||||
const SPIRType &type = msl.get_type(resource.type_id);
|
||||
const SPIRType &imagetype = msl.get_type(basetype.image.type);
|
||||
|
||||
UniformInfo u = {};
|
||||
u.baseType = baseType;
|
||||
u.name = resource.name;
|
||||
u.count = type.array.empty() ? 1 : type.array[0];
|
||||
u.isDepthSampler = type.image.depth;
|
||||
u.components = 1;
|
||||
|
||||
auto it = uniforms.find(u.name);
|
||||
if (it != uniforms.end())
|
||||
return;
|
||||
|
||||
if (!fillUniformReflectionData(u))
|
||||
return;
|
||||
|
||||
switch (imagetype.basetype)
|
||||
{
|
||||
case SPIRType::Float:
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
break;
|
||||
case SPIRType::Int:
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
break;
|
||||
case SPIRType::UInt:
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (basetype.image.dim)
|
||||
{
|
||||
case spv::Dim2D:
|
||||
u.textureType = basetype.image.arrayed ? TEXTURE_2D_ARRAY : TEXTURE_2D;
|
||||
u.textures = new love::graphics::Texture*[u.count];
|
||||
memset(u.textures, 0, sizeof(love::graphics::Texture *) * u.count);
|
||||
break;
|
||||
case spv::Dim3D:
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
u.textures = new love::graphics::Texture*[u.count];
|
||||
memset(u.textures, 0, sizeof(love::graphics::Texture *) * u.count);
|
||||
break;
|
||||
case spv::DimCube:
|
||||
if (basetype.image.arrayed)
|
||||
throw love::Exception("Cubemap Arrays are not currently supported.");
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
u.textures = new love::graphics::Texture*[u.count];
|
||||
memset(u.textures, 0, sizeof(love::graphics::Texture *) * u.count);
|
||||
break;
|
||||
case spv::DimBuffer:
|
||||
u.baseType = UNIFORM_TEXELBUFFER;
|
||||
u.buffers = new love::graphics::Buffer*[u.count];
|
||||
memset(u.buffers, 0, sizeof(love::graphics::Buffer *) * u.count);
|
||||
break;
|
||||
default:
|
||||
// TODO: error? continue?
|
||||
break;
|
||||
}
|
||||
|
||||
u.dataSize = sizeof(int) * u.count;
|
||||
u.data = malloc(u.dataSize);
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.ints[i] = -1; // Initialized below, after compiling.
|
||||
|
||||
uniforms[u.name] = u;
|
||||
|
||||
BuiltinUniform builtin;
|
||||
if (getConstant(resource.name.c_str(), builtin))
|
||||
builtinUniformInfo[builtin] = &uniforms[u.name];
|
||||
}
|
||||
|
||||
void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &program)
|
||||
{
|
||||
using namespace glslang;
|
||||
@@ -473,21 +386,10 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
|
||||
auto &msl = *mslpointer;
|
||||
|
||||
auto interfacevars = msl.get_active_interface_variables();
|
||||
ShaderResources resources = msl.get_shader_resources(interfacevars);
|
||||
|
||||
msl.set_enabled_interface_variables(interfacevars);
|
||||
|
||||
ShaderResources resources = msl.get_shader_resources();
|
||||
|
||||
for (const auto &resource : resources.storage_images)
|
||||
{
|
||||
addImage(msl, resource, UNIFORM_STORAGETEXTURE);
|
||||
}
|
||||
|
||||
for (const auto &resource : resources.sampled_images)
|
||||
{
|
||||
addImage(msl, resource, UNIFORM_SAMPLER);
|
||||
}
|
||||
|
||||
for (const auto &resource : resources.uniform_buffers)
|
||||
{
|
||||
MSLResourceBinding binding;
|
||||
@@ -535,33 +437,6 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
|
||||
binding.desc_set = msl.get_decoration(resource.id, spv::DecorationDescriptorSet);
|
||||
binding.msl_buffer = metalBufferIndices[stageindex]++;
|
||||
msl.add_msl_resource_binding(binding);
|
||||
|
||||
auto it = uniforms.find(resource.name);
|
||||
if (it != uniforms.end())
|
||||
continue;
|
||||
|
||||
const SPIRType &type = msl.get_type(resource.type_id);
|
||||
|
||||
UniformInfo u = {};
|
||||
u.baseType = UNIFORM_STORAGEBUFFER;
|
||||
u.components = 1;
|
||||
u.name = resource.name;
|
||||
u.count = type.array.empty() ? 1 : type.array[0];
|
||||
|
||||
if (!fillUniformReflectionData(u))
|
||||
continue;
|
||||
|
||||
u.buffers = new love::graphics::Buffer*[u.count];
|
||||
u.dataSize = sizeof(int) * u.count;
|
||||
u.data = malloc(u.dataSize);
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
u.ints[i] = -1; // Initialized below, after compiling.
|
||||
u.buffers[i] = nullptr;
|
||||
}
|
||||
|
||||
uniforms[u.name] = u;
|
||||
}
|
||||
|
||||
if (stageindex == SHADERSTAGE_VERTEX)
|
||||
@@ -634,7 +509,8 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
|
||||
if (library == nil && err != nil)
|
||||
{
|
||||
NSLog(@"errors: %@", err);
|
||||
throw love::Exception("Error compiling converted Metal shader code");
|
||||
NSString *errorstr = err.localizedDescription;
|
||||
throw love::Exception("Error compiling converted Metal shader code:\n\n%s", errorstr.UTF8String);
|
||||
}
|
||||
|
||||
functions[stageindex] = [library newFunctionWithName:library.functionNames[0]];
|
||||
@@ -645,11 +521,15 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
|
||||
|
||||
auto setTextureBinding = [this](CompilerMSL &msl, int stageindex, const spirv_cross::Resource &resource) -> void
|
||||
{
|
||||
auto it = uniforms.find(resource.name);
|
||||
if (it == uniforms.end())
|
||||
std::string name = canonicaliizeUniformName(resource.name);
|
||||
auto it = reflection.allUniforms.find(name);
|
||||
if (it == reflection.allUniforms.end())
|
||||
{
|
||||
handleUnknownUniformName(name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
UniformInfo &u = it->second;
|
||||
UniformInfo &u = *(it->second);
|
||||
|
||||
uint32 texturebinding = msl.get_automatic_msl_resource_binding(resource.id);
|
||||
uint32 samplerbinding = msl.get_automatic_msl_resource_binding_secondary(resource.id);
|
||||
@@ -657,15 +537,16 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
|
||||
if (texturebinding == (uint32)-1)
|
||||
{
|
||||
// No valid binding, the uniform was likely optimized out because it's not used.
|
||||
uniforms.erase(resource.name);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.active = true;
|
||||
|
||||
if (u.location < 0)
|
||||
{
|
||||
if (u.ints[i] == -1)
|
||||
u.location = (int)textureBindings.size();
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
u.ints[i] = (int)textureBindings.size();
|
||||
TextureBinding b = {};
|
||||
b.access = u.access;
|
||||
|
||||
@@ -683,11 +564,18 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
|
||||
|
||||
textureBindings.push_back(b);
|
||||
}
|
||||
}
|
||||
|
||||
auto &b = textureBindings[u.ints[i]];
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
auto &b = textureBindings[u.location + i];
|
||||
b.textureStages[stageindex] = (uint8) texturebinding;
|
||||
b.samplerStages[stageindex] = (uint8) samplerbinding;
|
||||
}
|
||||
|
||||
BuiltinUniform builtin;
|
||||
if (getConstant(name.c_str(), builtin))
|
||||
builtinUniformInfo[builtin] = &u;
|
||||
};
|
||||
|
||||
for (const auto &resource : resources.sampled_images)
|
||||
@@ -702,9 +590,13 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
|
||||
|
||||
for (const auto &resource : resources.storage_buffers)
|
||||
{
|
||||
auto it = uniforms.find(resource.name);
|
||||
if (it == uniforms.end())
|
||||
std::string name = canonicaliizeUniformName(resource.name);
|
||||
auto it = reflection.storageBuffers.find(name);
|
||||
if (it == reflection.storageBuffers.end())
|
||||
{
|
||||
handleUnknownUniformName(name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
UniformInfo &u = it->second;
|
||||
|
||||
@@ -712,15 +604,16 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
|
||||
if (bufferbinding == (uint32)-1)
|
||||
{
|
||||
// No valid binding, the uniform was likely optimized out because it's not used.
|
||||
uniforms.erase(resource.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.active = true;
|
||||
|
||||
if (u.location < 0)
|
||||
{
|
||||
if (u.ints[i] == -1)
|
||||
u.location = (int)bufferBindings.size();
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
u.ints[i] = (int)bufferBindings.size();
|
||||
BufferBinding b = {};
|
||||
b.access = u.access;
|
||||
|
||||
@@ -729,25 +622,26 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
|
||||
|
||||
bufferBindings.push_back(b);
|
||||
}
|
||||
|
||||
bufferBindings[u.ints[i]].stages[stageindex] = (uint8) bufferbinding;
|
||||
}
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
bufferBindings[u.location + i].stages[stageindex] = (uint8) bufferbinding;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize default resource bindings.
|
||||
for (auto &kvp : uniforms)
|
||||
for (const auto &kvp : reflection.allUniforms)
|
||||
{
|
||||
UniformInfo &info = kvp.second;
|
||||
switch (info.baseType)
|
||||
const UniformInfo *info = kvp.second;
|
||||
switch (info->baseType)
|
||||
{
|
||||
case UNIFORM_SAMPLER:
|
||||
case UNIFORM_STORAGETEXTURE:
|
||||
sendTextures(&info, info.textures, info.count);
|
||||
sendTextures(info, &activeTextures[info->resourceIndex], info->count);
|
||||
break;
|
||||
case UNIFORM_TEXELBUFFER:
|
||||
case UNIFORM_STORAGEBUFFER:
|
||||
sendBuffers(&info, info.buffers, info.count);
|
||||
sendBuffers(info, &activeBuffers[info->resourceIndex], info->count);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -767,31 +661,6 @@ Shader::~Shader()
|
||||
|
||||
cachedRenderPipelines.clear();
|
||||
|
||||
for (const auto &it : uniforms)
|
||||
{
|
||||
const auto &u = it.second;
|
||||
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
free(u.data);
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
if (u.textures[i] != nullptr)
|
||||
u.textures[i]->release();
|
||||
}
|
||||
delete[] u.textures;
|
||||
}
|
||||
else if (u.baseType == UNIFORM_TEXELBUFFER || u.baseType == UNIFORM_STORAGEBUFFER)
|
||||
{
|
||||
free(u.data);
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
if (u.buffers[i] != nullptr)
|
||||
u.buffers[i]->release();
|
||||
}
|
||||
delete[] u.buffers;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] localUniformStagingData;
|
||||
delete[] localUniformBufferData;
|
||||
}}
|
||||
@@ -813,12 +682,6 @@ int Shader::getVertexAttributeIndex(const std::string &name)
|
||||
return it != attributes.end() ? it->second : -1;
|
||||
}
|
||||
|
||||
const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
|
||||
{
|
||||
const auto it = uniforms.find(name);
|
||||
return it != uniforms.end() ? &(it->second) : nullptr;
|
||||
}
|
||||
|
||||
const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const
|
||||
{
|
||||
return builtinUniformInfo[(int)builtin];
|
||||
@@ -826,6 +689,9 @@ const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const
|
||||
|
||||
void Shader::updateUniform(const UniformInfo *info, int count)
|
||||
{
|
||||
if (info->dataSize == 0)
|
||||
return;
|
||||
|
||||
if (current == this)
|
||||
Graphics::flushBatchedDrawsGlobal();
|
||||
|
||||
@@ -895,12 +761,21 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
|
||||
|
||||
tex->retain();
|
||||
|
||||
if (info->textures[i] != nullptr)
|
||||
info->textures[i]->release();
|
||||
int resourceindex = info->resourceIndex + i;
|
||||
|
||||
info->textures[i] = tex;
|
||||
if (activeTextures[resourceindex] != nullptr)
|
||||
activeTextures[resourceindex]->release();
|
||||
|
||||
auto &binding = textureBindings[info->ints[i]];
|
||||
activeTextures[resourceindex] = tex;
|
||||
|
||||
if (info->location < 0)
|
||||
continue;
|
||||
|
||||
int bindingindex = info->location + i;
|
||||
if (bindingindex < 0)
|
||||
continue;
|
||||
|
||||
auto &binding = textureBindings[bindingindex];
|
||||
if (isdefault && (binding.access & ACCESS_WRITE) != 0)
|
||||
{
|
||||
binding.texture = nil;
|
||||
@@ -927,7 +802,6 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
||||
|
||||
count = std::min(count, info->count);
|
||||
|
||||
// Bind the textures to the texture units.
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
love::graphics::Buffer *buffer = buffers[i];
|
||||
@@ -949,18 +823,24 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
||||
|
||||
buffer->retain();
|
||||
|
||||
if (info->buffers[i] != nullptr)
|
||||
info->buffers[i]->release();
|
||||
int resourceindex = info->resourceIndex + i;
|
||||
|
||||
info->buffers[i] = buffer;
|
||||
if (activeBuffers[resourceindex] != nullptr)
|
||||
activeBuffers[resourceindex]->release();
|
||||
|
||||
if (texelbinding)
|
||||
activeBuffers[resourceindex] = buffer;
|
||||
|
||||
if (info->location < 0)
|
||||
continue;
|
||||
|
||||
int bindingindex = info->location + i;
|
||||
if (texelbinding && bindingindex >= 0)
|
||||
{
|
||||
textureBindings[info->ints[i]].texture = getMTLTexture(buffer);
|
||||
textureBindings[bindingindex].texture = getMTLTexture(buffer);
|
||||
}
|
||||
else if (storagebinding)
|
||||
else if (storagebinding && bindingindex >= 0)
|
||||
{
|
||||
auto &binding = bufferBindings[info->ints[i]];
|
||||
auto &binding = bufferBindings[bindingindex];
|
||||
if (isdefault && (binding.access & ACCESS_WRITE) != 0)
|
||||
binding.buffer = nil;
|
||||
else
|
||||
@@ -987,11 +867,6 @@ void Shader::setVideoTextures(love::graphics::Texture *ytexture, love::graphics:
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::hasUniform(const std::string &name) const
|
||||
{
|
||||
return uniforms.find(name) != uniforms.end();
|
||||
}
|
||||
|
||||
id<MTLRenderPipelineState> Shader::getCachedRenderPipeline(const RenderPipelineKey &key)
|
||||
{
|
||||
auto it = cachedRenderPipelines.find(key);
|
||||
@@ -1073,7 +948,7 @@ id<MTLRenderPipelineState> Shader::getCachedRenderPipeline(const RenderPipelineK
|
||||
const auto &attrib = attributes.attribs[i];
|
||||
int metalBufferIndex = firstVertexBufferBinding + attrib.bufferIndex;
|
||||
|
||||
vertdesc.attributes[i].format = getMTLVertexFormat(attrib.format);
|
||||
vertdesc.attributes[i].format = getMTLVertexFormat(attrib.getFormat());
|
||||
vertdesc.attributes[i].offset = attrib.offsetFromVertex;
|
||||
vertdesc.attributes[i].bufferIndex = metalBufferIndex;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -48,6 +48,8 @@ public:
|
||||
if (buffer == nil)
|
||||
throw love::Exception("Out of graphics memory.");
|
||||
|
||||
buffer.label = [NSString stringWithFormat:@"StreamBuffer (usage: %d, size: %ld)", usage, size];
|
||||
|
||||
data = (uint8 *) buffer.contents;
|
||||
|
||||
for (int i = 0; i < BUFFER_FRAMES; i++)
|
||||
@@ -65,6 +67,11 @@ public:
|
||||
}
|
||||
}}
|
||||
|
||||
size_t getGPUReadOffset() const override
|
||||
{
|
||||
return (frameIndex * bufferSize) + frameGPUReadOffset;
|
||||
}
|
||||
|
||||
MapInfo map(size_t /*minsize*/) override
|
||||
{
|
||||
// Make sure this frame's section of the buffer is done being used.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -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;
|
||||
@@ -55,14 +56,14 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
|
||||
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
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -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;
|
||||
|
||||
@@ -81,6 +77,15 @@ Texture::Texture(love::graphics::Graphics *gfxbase, id<MTLDevice> device, const
|
||||
if (computeWrite)
|
||||
desc.usage |= MTLTextureUsageShaderWrite;
|
||||
|
||||
for (PixelFormat viewformat : viewFormats)
|
||||
{
|
||||
if (getLinearPixelFormat(viewformat) != getLinearPixelFormat(format))
|
||||
{
|
||||
desc.usage |= MTLTextureUsagePixelFormatView;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
texture = [device newTextureWithDescriptor:desc];
|
||||
|
||||
if (texture == nil)
|
||||
@@ -149,7 +154,7 @@ Texture::Texture(love::graphics::Graphics *gfxbase, id<MTLDevice> device, const
|
||||
emptydata.resize(getPixelFormatSliceSize(format, w, h));
|
||||
|
||||
Rect r = {0, 0, getPixelWidth(mip), getPixelHeight(mip)};
|
||||
uploadByteData(format, emptydata.data(), emptydata.size(), mip, slice, r);
|
||||
uploadByteData(emptydata.data(), emptydata.size(), mip, slice, r);
|
||||
}
|
||||
else if (isRenderTarget())
|
||||
{
|
||||
@@ -212,6 +217,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;
|
||||
@@ -219,15 +259,13 @@ Texture::~Texture()
|
||||
sampler = nil;
|
||||
}}
|
||||
|
||||
void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r)
|
||||
void Texture::uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r)
|
||||
{ @autoreleasepool {
|
||||
auto gfx = Graphics::getInstance();
|
||||
id<MTLBuffer> buffer = [gfx->device newBufferWithBytes:data
|
||||
length:size
|
||||
options:MTLResourceStorageModeShared];
|
||||
|
||||
memcpy(buffer.contents, data, size);
|
||||
|
||||
id<MTLBlitCommandEncoder> encoder = gfx->useBlitEncoder();
|
||||
|
||||
int z = 0;
|
||||
@@ -239,7 +277,7 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
|
||||
|
||||
MTLBlitOption options = MTLBlitOptionNone;
|
||||
|
||||
switch (pixelformat)
|
||||
switch (format)
|
||||
{
|
||||
case PIXELFORMAT_PVR1_RGB2_UNORM:
|
||||
case PIXELFORMAT_PVR1_RGB4_UNORM:
|
||||
@@ -342,10 +380,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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -106,7 +106,8 @@ love::graphics::Graphics *createInstance()
|
||||
}
|
||||
|
||||
Graphics::Graphics()
|
||||
: windowHasStencil(false)
|
||||
: love::graphics::Graphics("love.graphics.opengl")
|
||||
, windowHasStencil(false)
|
||||
, mainVAO(0)
|
||||
, internalBackbufferFBO(0)
|
||||
, requestedBackbufferMSAA(0)
|
||||
@@ -147,11 +148,6 @@ Graphics::~Graphics()
|
||||
delete[] bufferMapMemory;
|
||||
}
|
||||
|
||||
const char *Graphics::getName() const
|
||||
{
|
||||
return "love.graphics.opengl";
|
||||
}
|
||||
|
||||
love::graphics::StreamBuffer *Graphics::newStreamBuffer(BufferUsage type, size_t size)
|
||||
{
|
||||
return CreateStreamBuffer(type, size);
|
||||
@@ -162,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);
|
||||
@@ -187,18 +188,6 @@ love::graphics::GraphicsReadback *Graphics::newReadbackInternal(ReadbackMethod m
|
||||
return new GraphicsReadback(this, method, texture, slice, mipmap, rect, dest, destx, desty);
|
||||
}
|
||||
|
||||
Matrix4 Graphics::computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const
|
||||
{
|
||||
uint32 flags = DEVICE_PROJECTION_DEFAULT;
|
||||
|
||||
// The projection matrix is flipped compared to rendering to a texture, due
|
||||
// to OpenGL considering (0,0) bottom-left instead of top-left.
|
||||
if (!rendertotexture)
|
||||
flags |= DEVICE_PROJECTION_FLIP_Y;
|
||||
|
||||
return calculateDeviceProjection(projection, flags);
|
||||
}
|
||||
|
||||
void Graphics::backbufferChanged(int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, bool backbufferdepth, int msaa)
|
||||
{
|
||||
bool changed = width != this->width || height != this->height
|
||||
@@ -707,24 +696,27 @@ void Graphics::drawQuads(int start, int count, const VertexAttributes &attribute
|
||||
|
||||
static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
|
||||
{
|
||||
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
|
||||
return;
|
||||
|
||||
// Human-readable strings for the debug info.
|
||||
const char *sourceStr = OpenGL::debugSourceString(source);
|
||||
const char *typeStr = OpenGL::debugTypeString(type);
|
||||
const char *severityStr = OpenGL::debugSeverityString(severity);
|
||||
|
||||
const char *fmt = "OpenGL: %s [source=%s, type=%s, severity=%s, id=%d]\n";
|
||||
printf(fmt, msg, sourceStr, typeStr, severityStr, id);
|
||||
const char *fmt = "OpenGL: [source=%s, type=%s, severity=%s, id=%d]: %s\n";
|
||||
printf(fmt, sourceStr, typeStr, severityStr, id, msg);
|
||||
}
|
||||
|
||||
void Graphics::setDebug(bool enable)
|
||||
{
|
||||
// Make sure debug output is supported. The AMD ext. is a bit different
|
||||
// so we don't make use of it, since AMD drivers now support KHR_debug.
|
||||
if (!(GLAD_VERSION_4_3 || GLAD_KHR_debug || GLAD_ARB_debug_output))
|
||||
if (!(GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_2 || GLAD_KHR_debug || GLAD_ARB_debug_output))
|
||||
return;
|
||||
|
||||
// TODO: We don't support GL_KHR_debug in GLES yet.
|
||||
if (GLAD_ES_VERSION_2_0)
|
||||
if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_2)
|
||||
return;
|
||||
|
||||
// Ugly hack to reduce code duplication.
|
||||
@@ -740,7 +732,7 @@ void Graphics::setDebug(bool enable)
|
||||
glDebugMessageCallback(nullptr, nullptr);
|
||||
|
||||
// We can disable debug output entirely with KHR_debug.
|
||||
if (GLAD_VERSION_4_3 || GLAD_KHR_debug)
|
||||
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_2 || GLAD_KHR_debug)
|
||||
glDisable(GL_DEBUG_OUTPUT);
|
||||
|
||||
return;
|
||||
@@ -758,7 +750,7 @@ void Graphics::setDebug(bool enable)
|
||||
glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
|
||||
glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
|
||||
|
||||
if (GLAD_VERSION_4_3 || GLAD_KHR_debug)
|
||||
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_2 || GLAD_KHR_debug)
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
|
||||
::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
|
||||
@@ -1689,6 +1681,17 @@ void Graphics::initCapabilities()
|
||||
pixelFormatUsage[i][0] = computePixelFormatUsage(format, false);
|
||||
pixelFormatUsage[i][1] = computePixelFormatUsage(format, true);
|
||||
}
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
// This can't be done in initContext with the rest of the bug checks because
|
||||
// isPixelFormatSupported relies on state initialized here / after init.
|
||||
if (GLAD_ES_VERSION_3_0 && !isPixelFormatSupported(PIXELFORMAT_R8_UNORM, PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET))
|
||||
{
|
||||
gl.bugs.brokenR8PixelFormat = true;
|
||||
pixelFormatUsage[PIXELFORMAT_R8_UNORM][0] = computePixelFormatUsage(PIXELFORMAT_R8_UNORM, false);
|
||||
pixelFormatUsage[PIXELFORMAT_R8_UNORM][1] = computePixelFormatUsage(PIXELFORMAT_R8_UNORM, true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32 Graphics::computePixelFormatUsage(PixelFormat format, bool readable)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -56,14 +56,10 @@ public:
|
||||
Graphics();
|
||||
virtual ~Graphics();
|
||||
|
||||
// Implements Module.
|
||||
const char *getName() const override;
|
||||
|
||||
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;
|
||||
|
||||
void backbufferChanged(int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, bool backbufferdepth, int msaa) override;
|
||||
bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, bool backbufferdepth, int msaa) override;
|
||||
void unSetMode() override;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -68,8 +68,6 @@ GraphicsReadback::GraphicsReadback(love::graphics::Graphics *gfx, ReadbackMethod
|
||||
{
|
||||
void *dest = prepareReadbackDest(size);
|
||||
|
||||
love::thread::Lock lock(imageData->getMutex());
|
||||
|
||||
// Direct readback without copying avoids the need for a staging buffer,
|
||||
// and lowers the system requirements of immediate RT readback.
|
||||
Texture *t = (Texture *) texture;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -324,14 +324,6 @@ void OpenGL::setupContext()
|
||||
setColorWriteMask(state.colorWriteMask);
|
||||
|
||||
contextInitialized = true;
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
// This can't be done in initContext with the rest of the bug checks because
|
||||
// isPixelFormatSupported relies on state initialized here / after init.
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
if (GLAD_ES_VERSION_3_0 && gfx != nullptr && !gfx->isPixelFormatSupported(PIXELFORMAT_R8_UNORM, PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET))
|
||||
bugs.brokenR8PixelFormat = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGL::deInitContext()
|
||||
@@ -870,7 +862,7 @@ void OpenGL::setVertexAttributes(const VertexAttributes &attributes, const Buffe
|
||||
int components = 0;
|
||||
GLboolean normalized = GL_FALSE;
|
||||
bool intformat = false;
|
||||
GLenum gltype = getGLVertexDataType(attrib.format, components, normalized, intformat);
|
||||
GLenum gltype = getGLVertexDataType(attrib.getFormat(), components, normalized, intformat);
|
||||
|
||||
const void *offsetpointer = reinterpret_cast<void*>(bufferinfo.offset + attrib.offsetFromVertex);
|
||||
|
||||
@@ -1344,7 +1336,17 @@ bool OpenGL::rawTexStorage(TextureType target, int levels, PixelFormat pixelform
|
||||
glTexParameteri(gltarget, GL_TEXTURE_SWIZZLE_A, fmt.swizzle[3]);
|
||||
}
|
||||
|
||||
if (isTexStorageSupported())
|
||||
bool usetexstorage = isTexStorageSupported();
|
||||
|
||||
// The fallback for bugs.brokenR8PixelFormat is GL_LUMINANCE, which doesn't have a sized
|
||||
// version in ES3 so it can't be used with glTexStorage.
|
||||
if (pixelformat == PIXELFORMAT_R8_UNORM && bugs.brokenR8PixelFormat && GLAD_ES_VERSION_3_0)
|
||||
{
|
||||
usetexstorage = false;
|
||||
fmt.internalformat = fmt.externalformat;
|
||||
}
|
||||
|
||||
if (usetexstorage)
|
||||
{
|
||||
if (target == TEXTURE_2D || target == TEXTURE_CUBE)
|
||||
glTexStorage2D(gltarget, levels, fmt.internalformat, width, height);
|
||||
@@ -2162,8 +2164,6 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0
|
||||
|| ((GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB) && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB)))
|
||||
flags |= commonrender;
|
||||
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_BGRA8_UNORM:
|
||||
case PIXELFORMAT_BGRA8_sRGB:
|
||||
@@ -2190,7 +2190,7 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= commonsample | commonrender;
|
||||
if (GLAD_ES_VERSION_3_0 || (GLAD_OES_texture_half_float && GLAD_EXT_texture_rg))
|
||||
flags |= commonsample;
|
||||
if (GLAD_EXT_color_buffer_half_float && (GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_rg))
|
||||
if ((GLAD_EXT_color_buffer_half_float || GLAD_EXT_color_buffer_float) && (GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_rg))
|
||||
flags |= commonrender;
|
||||
if (!(GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear))
|
||||
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
|
||||
@@ -2202,7 +2202,7 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= commonsample | commonrender;
|
||||
if (GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float)
|
||||
flags |= commonsample;
|
||||
if (GLAD_EXT_color_buffer_half_float)
|
||||
if (GLAD_EXT_color_buffer_half_float || GLAD_EXT_color_buffer_float)
|
||||
flags |= commonrender;
|
||||
if (!(GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear))
|
||||
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
|
||||
@@ -2218,6 +2218,8 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= commonsample | commonrender;
|
||||
if (GLAD_ES_VERSION_3_0 || (GLAD_OES_texture_float && GLAD_EXT_texture_rg))
|
||||
flags |= commonsample;
|
||||
if (GLAD_EXT_color_buffer_float)
|
||||
flags |= commonrender;
|
||||
if (!(GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float_linear))
|
||||
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
|
||||
if (GLAD_VERSION_4_3)
|
||||
@@ -2228,6 +2230,8 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= commonsample | commonrender;
|
||||
if (GLAD_ES_VERSION_3_0 || GLAD_OES_texture_float)
|
||||
flags |= commonsample;
|
||||
if (GLAD_EXT_color_buffer_float)
|
||||
flags |= commonrender;
|
||||
if (!(GLAD_VERSION_1_1 || GLAD_OES_texture_float_linear))
|
||||
flags &= ~PIXELFORMATUSAGEFLAGS_LINEAR;
|
||||
if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_1)
|
||||
@@ -2295,10 +2299,12 @@ uint32 OpenGL::getPixelFormatUsageFlags(PixelFormat pixelformat)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
case PIXELFORMAT_RG11B10_FLOAT:
|
||||
if (GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_texture_packed_float)
|
||||
if (GLAD_ES_VERSION_3_1 || GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_texture_packed_float)
|
||||
flags |= commonsample;
|
||||
if (GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_color_buffer_packed_float)
|
||||
flags |= commonrender;
|
||||
if (GLAD_EXT_color_buffer_float)
|
||||
flags |= commonrender;
|
||||
if (GLAD_VERSION_4_3)
|
||||
flags |= computewrite;
|
||||
break;
|
||||
@@ -2506,6 +2512,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";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -38,11 +38,6 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
static bool isBuffer(Shader::UniformType utype)
|
||||
{
|
||||
return utype == Shader::UNIFORM_TEXELBUFFER || utype == Shader::UNIFORM_STORAGEBUFFER;
|
||||
}
|
||||
|
||||
Shader::Shader(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM], const CompileOptions &options)
|
||||
: love::graphics::Shader(stages, options)
|
||||
, program(0)
|
||||
@@ -58,32 +53,11 @@ Shader::~Shader()
|
||||
{
|
||||
unloadVolatile();
|
||||
|
||||
for (const auto &p : uniforms)
|
||||
for (const auto &p : reflection.allUniforms)
|
||||
{
|
||||
// Allocated with malloc().
|
||||
if (p.second.data != nullptr)
|
||||
free(p.second.data);
|
||||
|
||||
if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
if (p.second.textures[i] != nullptr)
|
||||
p.second.textures[i]->release();
|
||||
}
|
||||
|
||||
delete[] p.second.textures;
|
||||
}
|
||||
else if (isBuffer(p.second.baseType))
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
if (p.second.buffers[i] != nullptr)
|
||||
p.second.buffers[i]->release();
|
||||
}
|
||||
|
||||
delete[] p.second.buffers;
|
||||
}
|
||||
if (p.second->data != nullptr)
|
||||
free(p.second->data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +70,26 @@ void Shader::mapActiveUniforms()
|
||||
builtinUniformInfo[i] = nullptr;
|
||||
}
|
||||
|
||||
// Make sure all stored resources have their Volatiles loaded before
|
||||
// the sendTextures/sendBuffers calls below, since they call getHandle().
|
||||
for (love::graphics::Texture *tex : activeTextures)
|
||||
{
|
||||
if (tex == nullptr)
|
||||
continue;
|
||||
Volatile *v = dynamic_cast<Volatile *>(tex);
|
||||
if (v != nullptr)
|
||||
v->loadVolatile();
|
||||
}
|
||||
|
||||
for (love::graphics::Buffer *buffer : activeBuffers)
|
||||
{
|
||||
if (buffer == nullptr)
|
||||
continue;
|
||||
Volatile *v = dynamic_cast<Volatile *>(buffer);
|
||||
if (v != nullptr)
|
||||
v->loadVolatile();
|
||||
}
|
||||
|
||||
GLint activeprogram = 0;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram);
|
||||
|
||||
@@ -107,43 +101,39 @@ void Shader::mapActiveUniforms()
|
||||
GLchar cname[256];
|
||||
const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar));
|
||||
|
||||
std::map<std::string, UniformInfo> olduniforms = uniforms;
|
||||
uniforms.clear();
|
||||
|
||||
auto gfx = Module::getInstance<love::graphics::Graphics>(Module::M_GRAPHICS);
|
||||
|
||||
for (int uindex = 0; uindex < numuniforms; uindex++)
|
||||
{
|
||||
GLsizei namelen = 0;
|
||||
GLenum gltype = 0;
|
||||
UniformInfo u = {};
|
||||
int count = 0;
|
||||
|
||||
glGetActiveUniform(program, (GLuint) uindex, bufsize, &namelen, &u.count, &gltype, cname);
|
||||
glGetActiveUniform(program, (GLuint) uindex, bufsize, &namelen, &count, &gltype, cname);
|
||||
|
||||
u.name = std::string(cname, (size_t) namelen);
|
||||
u.location = glGetUniformLocation(program, u.name.c_str());
|
||||
u.access = ACCESS_READ;
|
||||
computeUniformTypeInfo(gltype, u);
|
||||
std::string name(cname, (size_t) namelen);
|
||||
int location = glGetUniformLocation(program, name.c_str());
|
||||
|
||||
// glGetActiveUniform appends "[0]" to the end of array uniform names...
|
||||
if (u.name.length() > 3)
|
||||
if (location == -1)
|
||||
continue;
|
||||
|
||||
name = canonicaliizeUniformName(name);
|
||||
|
||||
const auto &uniformit = reflection.allUniforms.find(name);
|
||||
if (uniformit == reflection.allUniforms.end())
|
||||
{
|
||||
size_t findpos = u.name.find("[0]");
|
||||
if (findpos != std::string::npos && findpos == u.name.length() - 3)
|
||||
u.name.erase(u.name.length() - 3);
|
||||
handleUnknownUniformName(name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
UniformInfo &u = *uniformit->second;
|
||||
|
||||
u.active = true;
|
||||
u.location = location;
|
||||
|
||||
// If this is a built-in (LOVE-created) uniform, store the location.
|
||||
BuiltinUniform builtin = BUILTIN_MAX_ENUM;
|
||||
if (getConstant(u.name.c_str(), builtin))
|
||||
builtinUniforms[int(builtin)] = u.location;
|
||||
|
||||
if (u.location == -1)
|
||||
continue;
|
||||
|
||||
if (!fillUniformReflectionData(u))
|
||||
continue;
|
||||
|
||||
if ((u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN) || u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
TextureUnit unit;
|
||||
@@ -175,183 +165,51 @@ void Shader::mapActiveUniforms()
|
||||
storageTextureBindings.push_back(binding);
|
||||
}
|
||||
|
||||
// Make sure previously set uniform data is preserved, and shader-
|
||||
// initialized values are retrieved.
|
||||
auto oldu = olduniforms.find(u.name);
|
||||
if (oldu != olduniforms.end())
|
||||
if (u.dataSize == 0)
|
||||
{
|
||||
u.data = oldu->second.data;
|
||||
u.dataSize = oldu->second.dataSize;
|
||||
u.textures = oldu->second.textures;
|
||||
if (u.baseType == UNIFORM_MATRIX)
|
||||
u.dataSize = sizeof(uint32) * u.matrix.rows * u.matrix.columns * u.count;
|
||||
else
|
||||
u.dataSize = sizeof(uint32) * u.components * u.count;
|
||||
|
||||
updateUniform(&u, u.count, true);
|
||||
u.data = malloc(u.dataSize);
|
||||
memset(u.data, 0, u.dataSize);
|
||||
|
||||
const auto &valuesit = reflection.localUniformInitializerValues.find(u.name);
|
||||
if (valuesit != reflection.localUniformInitializerValues.end())
|
||||
{
|
||||
const auto &values = valuesit->second;
|
||||
if (!values.empty())
|
||||
memcpy(u.data, values.data(), std::min(u.dataSize, sizeof(LocalUniformValue) * values.size()));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
u.dataSize = 0;
|
||||
int startunit = (int) textureUnits.size() - u.count;
|
||||
|
||||
switch (u.baseType)
|
||||
{
|
||||
case UNIFORM_FLOAT:
|
||||
u.dataSize = sizeof(float) * u.components * u.count;
|
||||
u.data = malloc(u.dataSize);
|
||||
break;
|
||||
case UNIFORM_INT:
|
||||
case UNIFORM_BOOL:
|
||||
case UNIFORM_SAMPLER:
|
||||
case UNIFORM_STORAGETEXTURE:
|
||||
case UNIFORM_TEXELBUFFER:
|
||||
u.dataSize = sizeof(int) * u.components * u.count;
|
||||
u.data = malloc(u.dataSize);
|
||||
break;
|
||||
case UNIFORM_UINT:
|
||||
u.dataSize = sizeof(unsigned int) * u.components * u.count;
|
||||
u.data = malloc(u.dataSize);
|
||||
break;
|
||||
case UNIFORM_MATRIX:
|
||||
u.dataSize = sizeof(float) * ((size_t)u.matrix.rows * u.matrix.columns) * u.count;
|
||||
u.data = malloc(u.dataSize);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (builtin == BUILTIN_TEXTURE_MAIN)
|
||||
startunit = 0;
|
||||
|
||||
if (u.dataSize > 0)
|
||||
{
|
||||
memset(u.data, 0, u.dataSize);
|
||||
|
||||
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
int startunit = (int) textureUnits.size() - u.count;
|
||||
|
||||
if (builtin == BUILTIN_TEXTURE_MAIN)
|
||||
startunit = 0;
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.ints[i] = startunit + i;
|
||||
|
||||
glUniform1iv(u.location, u.count, u.ints);
|
||||
|
||||
if (u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
u.buffers = new love::graphics::Buffer*[u.count];
|
||||
memset(u.buffers, 0, sizeof(Buffer *) * u.count);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.textures = new love::graphics::Texture*[u.count];
|
||||
|
||||
auto *tex = gfx->getDefaultTexture(u.textureType, u.dataBaseType);
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
tex->retain();
|
||||
u.textures[i] = tex;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (u.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
int startbinding = (int) storageTextureBindings.size() - u.count;
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.ints[i] = startbinding + i;
|
||||
|
||||
glUniform1iv(u.location, u.count, u.ints);
|
||||
|
||||
u.textures = new love::graphics::Texture*[u.count];
|
||||
|
||||
if ((u.access & ACCESS_WRITE) != 0)
|
||||
{
|
||||
memset(u.textures, 0, sizeof(Texture *) * u.count);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto *tex = gfx->getDefaultTexture(u.textureType, u.dataBaseType);
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
tex->retain();
|
||||
u.textures[i] = tex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
// Store any shader-initialized values in our own memory.
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
GLint location = u.location;
|
||||
|
||||
if (u.count > 1)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << i;
|
||||
|
||||
std::string indexname = u.name + "[" + ss.str() + "]";
|
||||
location = glGetUniformLocation(program, indexname.c_str());
|
||||
}
|
||||
|
||||
if (location == -1)
|
||||
continue;
|
||||
|
||||
switch (u.baseType)
|
||||
{
|
||||
case UNIFORM_FLOAT:
|
||||
glGetUniformfv(program, location, &u.floats[offset]);
|
||||
offset += u.components;
|
||||
break;
|
||||
case UNIFORM_INT:
|
||||
case UNIFORM_BOOL:
|
||||
glGetUniformiv(program, location, &u.ints[offset]);
|
||||
offset += u.components;
|
||||
break;
|
||||
case UNIFORM_UINT:
|
||||
glGetUniformuiv(program, location, &u.uints[offset]);
|
||||
offset += u.components;
|
||||
break;
|
||||
case UNIFORM_MATRIX:
|
||||
glGetUniformfv(program, location, &u.floats[offset]);
|
||||
offset += (size_t)u.matrix.rows * u.matrix.columns;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
u.ints[i] = startunit + i;
|
||||
}
|
||||
else if (u.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
int startbinding = (int) storageTextureBindings.size() - u.count;
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.ints[i] = startbinding + i;
|
||||
}
|
||||
|
||||
uniforms[u.name] = u;
|
||||
updateUniform(&u, u.count, true);
|
||||
|
||||
if (builtin != BUILTIN_MAX_ENUM)
|
||||
builtinUniformInfo[(int)builtin] = &uniforms[u.name];
|
||||
builtinUniformInfo[(int)builtin] = &u;
|
||||
|
||||
if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
// Make sure all stored textures have their Volatiles loaded before
|
||||
// the sendTextures call, since it calls getHandle().
|
||||
for (int i = 0; i < u.count; i++)
|
||||
{
|
||||
if (u.textures[i] == nullptr)
|
||||
continue;
|
||||
Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
|
||||
if (v != nullptr)
|
||||
v->loadVolatile();
|
||||
}
|
||||
|
||||
sendTextures(&u, u.textures, u.count, true);
|
||||
}
|
||||
sendTextures(&u, &activeTextures[u.resourceIndex], u.count, true);
|
||||
else if (u.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
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);
|
||||
}
|
||||
sendBuffers(&u, &activeBuffers[u.resourceIndex], u.count, true);
|
||||
}
|
||||
|
||||
if (gl.isBufferUsageSupported(BUFFERUSAGE_SHADER_STORAGE))
|
||||
@@ -364,37 +222,28 @@ void Shader::mapActiveUniforms()
|
||||
|
||||
for (int sindex = 0; sindex < numstoragebuffers; sindex++)
|
||||
{
|
||||
UniformInfo u = {};
|
||||
u.baseType = UNIFORM_STORAGEBUFFER;
|
||||
u.access = ACCESS_READ;
|
||||
|
||||
GLsizei namelength = 0;
|
||||
glGetProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, sindex, 2048, &namelength, namebuffer);
|
||||
|
||||
u.name = std::string(namebuffer, namelength);
|
||||
u.count = 1;
|
||||
std::string name = canonicaliizeUniformName(std::string(namebuffer, namelength));
|
||||
|
||||
if (!fillUniformReflectionData(u))
|
||||
const auto &uniformit = reflection.storageBuffers.find(name);
|
||||
if (uniformit == reflection.storageBuffers.end())
|
||||
{
|
||||
handleUnknownUniformName(name.c_str());
|
||||
continue;
|
||||
|
||||
// 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
|
||||
|
||||
UniformInfo &u = uniformit->second;
|
||||
|
||||
u.active = true;
|
||||
|
||||
if (u.dataSize == 0)
|
||||
{
|
||||
u.dataSize = sizeof(int) * 1;
|
||||
u.dataSize = sizeof(int) * u.count;
|
||||
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);
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.ints[i] = -1;
|
||||
}
|
||||
|
||||
// Unlike local uniforms and attributes, OpenGL doesn't auto-assign storage
|
||||
@@ -417,56 +266,13 @@ void Shader::mapActiveUniforms()
|
||||
if (u.access & ACCESS_WRITE)
|
||||
{
|
||||
p.second = (int)activeWritableStorageBuffers.size();
|
||||
activeWritableStorageBuffers.push_back(u.buffers[0]);
|
||||
activeWritableStorageBuffers.push_back(activeBuffers[u.resourceIndex]);
|
||||
}
|
||||
|
||||
storageBufferBindingIndexToActiveBinding[binding.bindingindex] = p;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
if (p.second.data != nullptr)
|
||||
free(p.second.data);
|
||||
|
||||
if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE)
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
if (p.second.textures[i] != nullptr)
|
||||
p.second.textures[i]->release();
|
||||
}
|
||||
|
||||
delete[] p.second.textures;
|
||||
}
|
||||
else if (isBuffer(p.second.baseType))
|
||||
{
|
||||
for (int i = 0; i < p.second.count; i++)
|
||||
{
|
||||
if (p.second.buffers[i] != nullptr)
|
||||
p.second.buffers[i]->release();
|
||||
}
|
||||
|
||||
delete[] p.second.buffers;
|
||||
}
|
||||
sendBuffers(&u, &activeBuffers[u.resourceIndex], u.count, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,7 +406,7 @@ std::string Shader::getWarnings() const
|
||||
|
||||
const std::string &stagewarnings = stage->getWarnings();
|
||||
|
||||
if (ShaderStage::getConstant(stage->getStageType(), stagestr))
|
||||
if (!stagewarnings.empty() && ShaderStage::getConstant(stage->getStageType(), stagestr))
|
||||
warnings += std::string(stagestr) + std::string(" shader:\n") + stagewarnings;
|
||||
}
|
||||
|
||||
@@ -649,16 +455,6 @@ void Shader::attach()
|
||||
}
|
||||
}
|
||||
|
||||
const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
|
||||
{
|
||||
const auto it = uniforms.find(name);
|
||||
|
||||
if (it == uniforms.end())
|
||||
return nullptr;
|
||||
|
||||
return &(it->second);
|
||||
}
|
||||
|
||||
const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const
|
||||
{
|
||||
return builtinUniformInfo[(int)builtin];
|
||||
@@ -802,10 +598,12 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
|
||||
|
||||
tex->retain();
|
||||
|
||||
if (info->textures[i] != nullptr)
|
||||
info->textures[i]->release();
|
||||
int resourceindex = info->resourceIndex + i;
|
||||
|
||||
info->textures[i] = tex;
|
||||
if (activeTextures[resourceindex] != nullptr)
|
||||
activeTextures[resourceindex]->release();
|
||||
|
||||
activeTextures[resourceindex] = tex;
|
||||
|
||||
if (isstoragetex)
|
||||
{
|
||||
@@ -885,10 +683,12 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
||||
|
||||
buffer->retain();
|
||||
|
||||
if (info->buffers[i] != nullptr)
|
||||
info->buffers[i]->release();
|
||||
int resourceindex = info->resourceIndex + i;
|
||||
|
||||
info->buffers[i] = buffer;
|
||||
if (activeBuffers[resourceindex] != nullptr)
|
||||
activeBuffers[resourceindex]->release();
|
||||
|
||||
activeBuffers[resourceindex] = buffer;
|
||||
|
||||
if (texelbinding)
|
||||
{
|
||||
@@ -926,11 +726,6 @@ void Shader::flushBatchedDraws() const
|
||||
Graphics::flushBatchedDrawsGlobal();
|
||||
}
|
||||
|
||||
bool Shader::hasUniform(const std::string &name) const
|
||||
{
|
||||
return uniforms.find(name) != uniforms.end();
|
||||
}
|
||||
|
||||
ptrdiff_t Shader::getHandle() const
|
||||
{
|
||||
return program;
|
||||
@@ -972,6 +767,8 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
|
||||
if (current != this)
|
||||
return;
|
||||
|
||||
bool rt = gfx->isRenderTargetActive();
|
||||
|
||||
BuiltinUniformData data;
|
||||
|
||||
data.transformMatrix = gfx->getTransform();
|
||||
@@ -997,13 +794,26 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
|
||||
// Same with point size.
|
||||
data.normalMatrix[1].w = gfx->getPointSize();
|
||||
|
||||
// Users expect to work with y-up NDC, y-down pixel coordinates and textures
|
||||
// (see graphics/Shader.h).
|
||||
// OpenGL has y-up NDC and y-up pixel coordinates and textures. If we just flip
|
||||
// NDC y when rendering to a texture, it's enough to make (0, 0) on the texture
|
||||
// match what we expect when sampling from it - so it's the same as if textures
|
||||
// are y-down with y-up NDC.
|
||||
// Windowing systems treat (0, 0) on the backbuffer texture as the bottom left,
|
||||
// so we don't need to do that there.
|
||||
uint32 clipflags = 0;
|
||||
if (rt)
|
||||
clipflags |= CLIP_TRANSFORM_FLIP_Y;
|
||||
data.clipSpaceParams = computeClipSpaceParams(clipflags);
|
||||
|
||||
data.screenSizeParams.x = viewportW;
|
||||
data.screenSizeParams.y = viewportH;
|
||||
|
||||
// The shader does pixcoord.y = gl_FragCoord.y * params.z + params.w.
|
||||
// This lets us flip pixcoord.y when needed, to be consistent (drawing
|
||||
// with no RT active makes the pixel coordinates y-flipped.)
|
||||
if (gfx->isRenderTargetActive())
|
||||
if (rt)
|
||||
{
|
||||
// No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
|
||||
data.screenSizeParams.z = 1.0f;
|
||||
@@ -1030,7 +840,7 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
|
||||
{
|
||||
GLint location = builtinUniforms[BUILTIN_UNIFORMS_PER_DRAW];
|
||||
if (location >= 0)
|
||||
glUniform4fv(location, 12, (const GLfloat *) &data);
|
||||
glUniform4fv(location, 13, (const GLfloat *) &data);
|
||||
GLint location2 = builtinUniforms[BUILTIN_UNIFORMS_PER_DRAW_2];
|
||||
if (location2 >= 0)
|
||||
glUniform4fv(location2, 1, (const GLfloat *) &data.screenSizeParams);
|
||||
@@ -1039,294 +849,7 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
|
||||
{
|
||||
GLint location = builtinUniforms[BUILTIN_UNIFORMS_PER_DRAW];
|
||||
if (location >= 0)
|
||||
glUniform4fv(location, 13, (const GLfloat *) &data);
|
||||
}
|
||||
}
|
||||
|
||||
int Shader::getUniformTypeComponents(GLenum type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_INT:
|
||||
case GL_UNSIGNED_INT:
|
||||
case GL_FLOAT:
|
||||
case GL_BOOL:
|
||||
return 1;
|
||||
case GL_INT_VEC2:
|
||||
case GL_UNSIGNED_INT_VEC2:
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_FLOAT_MAT2:
|
||||
case GL_BOOL_VEC2:
|
||||
return 2;
|
||||
case GL_INT_VEC3:
|
||||
case GL_UNSIGNED_INT_VEC3:
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_FLOAT_MAT3:
|
||||
case GL_BOOL_VEC3:
|
||||
return 3;
|
||||
case GL_INT_VEC4:
|
||||
case GL_UNSIGNED_INT_VEC4:
|
||||
case GL_FLOAT_VEC4:
|
||||
case GL_FLOAT_MAT4:
|
||||
case GL_BOOL_VEC4:
|
||||
return 4;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
Shader::MatrixSize Shader::getMatrixSize(GLenum type) const
|
||||
{
|
||||
MatrixSize m;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GL_FLOAT_MAT2:
|
||||
m.columns = m.rows = 2;
|
||||
break;
|
||||
case GL_FLOAT_MAT3:
|
||||
m.columns = m.rows = 3;
|
||||
break;
|
||||
case GL_FLOAT_MAT4:
|
||||
m.columns = m.rows = 4;
|
||||
break;
|
||||
case GL_FLOAT_MAT2x3:
|
||||
m.columns = 2;
|
||||
m.rows = 3;
|
||||
break;
|
||||
case GL_FLOAT_MAT2x4:
|
||||
m.columns = 2;
|
||||
m.rows = 4;
|
||||
break;
|
||||
case GL_FLOAT_MAT3x2:
|
||||
m.columns = 3;
|
||||
m.rows = 2;
|
||||
break;
|
||||
case GL_FLOAT_MAT3x4:
|
||||
m.columns = 3;
|
||||
m.rows = 4;
|
||||
break;
|
||||
case GL_FLOAT_MAT4x2:
|
||||
m.columns = 4;
|
||||
m.rows = 2;
|
||||
break;
|
||||
case GL_FLOAT_MAT4x3:
|
||||
m.columns = 4;
|
||||
m.rows = 3;
|
||||
break;
|
||||
default:
|
||||
m.columns = m.rows = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void Shader::computeUniformTypeInfo(GLenum type, UniformInfo &u)
|
||||
{
|
||||
u.isDepthSampler = false;
|
||||
u.components = getUniformTypeComponents(type);
|
||||
u.baseType = UNIFORM_UNKNOWN;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GL_INT:
|
||||
case GL_INT_VEC2:
|
||||
case GL_INT_VEC3:
|
||||
case GL_INT_VEC4:
|
||||
u.baseType = UNIFORM_INT;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
break;
|
||||
case GL_UNSIGNED_INT:
|
||||
case GL_UNSIGNED_INT_VEC2:
|
||||
case GL_UNSIGNED_INT_VEC3:
|
||||
case GL_UNSIGNED_INT_VEC4:
|
||||
u.baseType = UNIFORM_UINT;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
break;
|
||||
case GL_FLOAT:
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_FLOAT_VEC4:
|
||||
u.baseType = UNIFORM_FLOAT;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
break;
|
||||
case GL_FLOAT_MAT2:
|
||||
case GL_FLOAT_MAT3:
|
||||
case GL_FLOAT_MAT4:
|
||||
case GL_FLOAT_MAT2x3:
|
||||
case GL_FLOAT_MAT2x4:
|
||||
case GL_FLOAT_MAT3x2:
|
||||
case GL_FLOAT_MAT3x4:
|
||||
case GL_FLOAT_MAT4x2:
|
||||
case GL_FLOAT_MAT4x3:
|
||||
u.baseType = UNIFORM_MATRIX;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.matrix = getMatrixSize(type);
|
||||
break;
|
||||
case GL_BOOL:
|
||||
case GL_BOOL_VEC2:
|
||||
case GL_BOOL_VEC3:
|
||||
case GL_BOOL_VEC4:
|
||||
u.baseType = UNIFORM_BOOL;
|
||||
u.dataBaseType = DATA_BASETYPE_BOOL;
|
||||
break;
|
||||
|
||||
case GL_SAMPLER_2D:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
break;
|
||||
case GL_SAMPLER_2D_SHADOW:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
u.isDepthSampler = true;
|
||||
break;
|
||||
case GL_INT_SAMPLER_2D:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
break;
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
break;
|
||||
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
u.isDepthSampler = true;
|
||||
break;
|
||||
case GL_INT_SAMPLER_2D_ARRAY:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
break;
|
||||
case GL_SAMPLER_3D:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
break;
|
||||
case GL_INT_SAMPLER_3D:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_SAMPLER_3D:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
break;
|
||||
case GL_SAMPLER_CUBE:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
break;
|
||||
case GL_SAMPLER_CUBE_SHADOW:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
u.isDepthSampler = true;
|
||||
break;
|
||||
case GL_INT_SAMPLER_CUBE:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_SAMPLER_CUBE:
|
||||
u.baseType = UNIFORM_SAMPLER;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
break;
|
||||
|
||||
case GL_SAMPLER_BUFFER:
|
||||
u.baseType = UNIFORM_TEXELBUFFER;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
break;
|
||||
case GL_INT_SAMPLER_BUFFER:
|
||||
u.baseType = UNIFORM_TEXELBUFFER;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
|
||||
u.baseType = UNIFORM_TEXELBUFFER;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
break;
|
||||
|
||||
case GL_IMAGE_2D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
break;
|
||||
case GL_INT_IMAGE_2D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_IMAGE_2D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_2D;
|
||||
break;
|
||||
case GL_IMAGE_2D_ARRAY:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
break;
|
||||
case GL_INT_IMAGE_2D_ARRAY:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_2D_ARRAY;
|
||||
break;
|
||||
case GL_IMAGE_3D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
break;
|
||||
case GL_INT_IMAGE_3D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_IMAGE_3D:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_VOLUME;
|
||||
break;
|
||||
case GL_IMAGE_CUBE:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
break;
|
||||
case GL_INT_IMAGE_CUBE:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_INT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
break;
|
||||
case GL_UNSIGNED_INT_IMAGE_CUBE:
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.dataBaseType = DATA_BASETYPE_UINT;
|
||||
u.textureType = TEXTURE_CUBE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
glUniform4fv(location, 14, (const GLfloat *) &data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -63,12 +63,10 @@ public:
|
||||
void attach() override;
|
||||
std::string getWarnings() const override;
|
||||
int getVertexAttributeIndex(const std::string &name) override;
|
||||
const UniformInfo *getUniformInfo(const std::string &name) const override;
|
||||
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
|
||||
void updateUniform(const UniformInfo *info, int count) override;
|
||||
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override;
|
||||
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
|
||||
bool hasUniform(const std::string &name) const override;
|
||||
ptrdiff_t getHandle() const override;
|
||||
void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;
|
||||
|
||||
@@ -100,10 +98,6 @@ private:
|
||||
void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalupdate);
|
||||
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalupdate);
|
||||
|
||||
int getUniformTypeComponents(GLenum type) const;
|
||||
void computeUniformTypeInfo(GLenum type, UniformInfo &u);
|
||||
MatrixSize getMatrixSize(GLenum type) const;
|
||||
|
||||
void flushBatchedDraws() const;
|
||||
|
||||
// Get any warnings or errors generated only by the shader program object.
|
||||
@@ -120,9 +114,6 @@ private:
|
||||
|
||||
std::map<std::string, GLint> attributes;
|
||||
|
||||
// Uniform location buffer map
|
||||
std::map<std::string, UniformInfo> uniforms;
|
||||
|
||||
// Texture unit pool for setting textures
|
||||
std::vector<TextureUnit> textureUnits;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -63,6 +63,11 @@ public:
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
size_t getGPUReadOffset() const override
|
||||
{
|
||||
return (size_t) data;
|
||||
}
|
||||
|
||||
MapInfo map(size_t /*minsize*/) override
|
||||
{
|
||||
return MapInfo(data, bufferSize);
|
||||
@@ -111,6 +116,11 @@ public:
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
size_t getGPUReadOffset() const override
|
||||
{
|
||||
return frameGPUReadOffset;
|
||||
}
|
||||
|
||||
MapInfo map(size_t /*minsize*/) override
|
||||
{
|
||||
if (orphan)
|
||||
@@ -192,6 +202,11 @@ public:
|
||||
|
||||
virtual ~StreamBufferSync() {}
|
||||
|
||||
size_t getGPUReadOffset() const override
|
||||
{
|
||||
return (frameIndex * bufferSize) + frameGPUReadOffset;
|
||||
}
|
||||
|
||||
void nextFrame() override
|
||||
{
|
||||
// Insert a GPU fence for this frame's section of the data, we'll wait
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -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);
|
||||
|
||||
@@ -356,7 +390,7 @@ void Texture::createTexture()
|
||||
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);
|
||||
uploadByteData(emptydata.data(), emptydata.size(), mip, i, r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -466,19 +506,19 @@ void Texture::unloadVolatile()
|
||||
setGraphicsMemorySize(0);
|
||||
}
|
||||
|
||||
void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r)
|
||||
void Texture::uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r)
|
||||
{
|
||||
OpenGL::TempDebugGroup debuggroup("Texture data upload");
|
||||
|
||||
gl.bindTextureToUnit(this, 0, false);
|
||||
|
||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, false);
|
||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false);
|
||||
GLenum gltarget = OpenGL::getGLTextureType(texType);
|
||||
|
||||
if (texType == TEXTURE_CUBE)
|
||||
gltarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;
|
||||
|
||||
if (isPixelFormatCompressed(pixelformat))
|
||||
if (isPixelFormatCompressed(format))
|
||||
{
|
||||
if (texType == TEXTURE_2D || texType == TEXTURE_CUBE)
|
||||
{
|
||||
@@ -566,7 +606,7 @@ void Texture::copyFromBuffer(love::graphics::Buffer *source, size_t sourceoffset
|
||||
// glTexSubImage and friends copy from the active pixel_unpack_buffer by
|
||||
// treating the pointer as a byte offset.
|
||||
const uint8 *byteoffset = (const uint8 *)(ptrdiff_t)sourceoffset;
|
||||
uploadByteData(format, byteoffset, size, mipmap, slice, rect);
|
||||
uploadByteData(byteoffset, size, mipmap, slice, rect);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
@@ -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))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -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,9 +62,10 @@ public:
|
||||
void readbackInternal(int slice, int mipmap, const Rect &rect, int destwidth, size_t size, void *dest);
|
||||
|
||||
private:
|
||||
|
||||
void createTexture();
|
||||
|
||||
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
|
||||
void uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r) override;
|
||||
|
||||
void generateMipmapsInternal() override;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -26,6 +26,8 @@ namespace love
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
static_assert(sizeof(VertexAttributeInfo) == 4, "Unexpected sizeof(VertexAttributeInfo)");
|
||||
|
||||
static_assert(sizeof(Color32) == 4, "sizeof(Color32) incorrect!");
|
||||
static_assert(sizeof(STf_RGBAub) == sizeof(float)*2 + sizeof(Color32), "sizeof(STf_RGBAub) incorrect!");
|
||||
static_assert(sizeof(STPf_RGBAub) == sizeof(float)*3 + sizeof(Color32), "sizeof(STPf_RGBAub) incorrect!");
|
||||
@@ -336,7 +338,7 @@ bool VertexAttributes::operator == (const VertexAttributes &other) const
|
||||
{
|
||||
const auto &a = attribs[i];
|
||||
const auto &b = other.attribs[i];
|
||||
if (a.bufferIndex != b.bufferIndex || a.format != b.format || a.offsetFromVertex != b.offsetFromVertex)
|
||||
if (a.bufferIndex != b.bufferIndex || a.packedFormat != b.packedFormat || a.offsetFromVertex != b.offsetFromVertex)
|
||||
return false;
|
||||
|
||||
if (bufferLayouts[a.bufferIndex].stride != other.bufferLayouts[a.bufferIndex].stride)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -298,9 +298,12 @@ struct BufferBindings
|
||||
|
||||
struct VertexAttributeInfo
|
||||
{
|
||||
uint8 bufferIndex;
|
||||
DataFormat format : 8;
|
||||
uint16 offsetFromVertex;
|
||||
uint8 packedFormat;
|
||||
uint8 bufferIndex;
|
||||
|
||||
void setFormat(DataFormat format) { packedFormat = (uint8)format; }
|
||||
DataFormat getFormat() const { return (DataFormat)packedFormat; }
|
||||
};
|
||||
|
||||
struct VertexBufferLayout
|
||||
@@ -335,7 +338,7 @@ struct VertexAttributes
|
||||
enableBits |= (1u << index);
|
||||
|
||||
attribs[index].bufferIndex = bufferindex;
|
||||
attribs[index].format = format;
|
||||
attribs[index].setFormat(format);
|
||||
attribs[index].offsetFromVertex = offsetfromvertex;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "common/Exception.h"
|
||||
#include "common/pixelformat.h"
|
||||
#include "common/version.h"
|
||||
#include "common/memory.h"
|
||||
#include "window/Window.h"
|
||||
#include "Buffer.h"
|
||||
#include "Graphics.h"
|
||||
@@ -60,11 +61,6 @@ static const std::vector<const char*> deviceExtensions = {
|
||||
|
||||
constexpr uint32_t USAGES_POLL_INTERVAL = 5000;
|
||||
|
||||
const char *Graphics::getName() const
|
||||
{
|
||||
return "love.graphics.vulkan";
|
||||
}
|
||||
|
||||
VkDevice Graphics::getDevice() const
|
||||
{
|
||||
return device;
|
||||
@@ -95,6 +91,7 @@ static void checkOptionalInstanceExtensions(OptionalInstanceExtensions& ext)
|
||||
}
|
||||
|
||||
Graphics::Graphics()
|
||||
: love::graphics::Graphics("love.graphics.vulkan")
|
||||
{
|
||||
if (SDL_Vulkan_LoadLibrary(nullptr))
|
||||
throw love::Exception("could not find vulkan");
|
||||
@@ -157,6 +154,7 @@ Graphics::~Graphics()
|
||||
{
|
||||
defaultConstantTexCoord.set(nullptr);
|
||||
defaultConstantColor.set(nullptr);
|
||||
localUniformBuffer.set(nullptr);
|
||||
|
||||
Volatile::unloadAll();
|
||||
cleanup();
|
||||
@@ -172,6 +170,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);
|
||||
@@ -383,63 +386,50 @@ void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallback
|
||||
if (renderPassState.active)
|
||||
endRenderPass();
|
||||
|
||||
VkBuffer screenshotBuffer = VK_NULL_HANDLE;
|
||||
VmaAllocation screenshotAllocation = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo screenshotAllocationInfo = {};
|
||||
|
||||
if (submitMode == SUBMIT_PRESENT)
|
||||
{
|
||||
if (pendingScreenshotCallbacks.empty())
|
||||
Vulkan::cmdTransitionImageLayout(
|
||||
commandBuffers.at(currentFrame),
|
||||
swapChainImages.at(imageIndex),
|
||||
swapChainPixelFormat,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
else
|
||||
{
|
||||
VkBufferCreateInfo bufferInfo{};
|
||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferInfo.size = 4ll * swapChainExtent.width * swapChainExtent.height;
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo{};
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
|
||||
auto result = vmaCreateBuffer(
|
||||
vmaAllocator,
|
||||
&bufferInfo,
|
||||
&allocCreateInfo,
|
||||
&screenshotBuffer,
|
||||
&screenshotAllocation,
|
||||
&screenshotAllocationInfo);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
throw love::Exception("failed to create screenshot readback buffer");
|
||||
|
||||
// TODO: swap chain images aren't guaranteed to support TRANSFER_SRC_BIT usage flags.
|
||||
Vulkan::cmdTransitionImageLayout(
|
||||
commandBuffers.at(currentFrame),
|
||||
swapChainImages.at(imageIndex),
|
||||
swapChainPixelFormat,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(
|
||||
commandBuffers.at(currentFrame),
|
||||
screenshotReadbackBuffers.at(currentFrame).image,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
VkImageBlit blit{};
|
||||
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
blit.srcSubresource.layerCount = 1;
|
||||
blit.srcOffsets[1] = {
|
||||
static_cast<int>(swapChainExtent.width),
|
||||
static_cast<int>(swapChainExtent.height),
|
||||
1
|
||||
};
|
||||
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
blit.dstSubresource.layerCount = 1;
|
||||
blit.dstOffsets[1] = {
|
||||
static_cast<int>(swapChainExtent.width),
|
||||
static_cast<int>(swapChainExtent.height),
|
||||
1
|
||||
};
|
||||
|
||||
vkCmdBlitImage(
|
||||
commandBuffers.at(currentFrame),
|
||||
swapChainImages.at(imageIndex), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
screenshotReadbackBuffers.at(currentFrame).image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, &blit,
|
||||
VK_FILTER_NEAREST);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(
|
||||
commandBuffers.at(currentFrame),
|
||||
swapChainImages.at(imageIndex),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(
|
||||
commandBuffers.at(currentFrame),
|
||||
screenshotReadbackBuffers.at(currentFrame).image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
VkBufferImageCopy region{};
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
@@ -451,32 +441,17 @@ void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallback
|
||||
|
||||
vkCmdCopyImageToBuffer(
|
||||
commandBuffers.at(currentFrame),
|
||||
screenshotReadbackBuffers.at(currentFrame).image,
|
||||
swapChainImages.at(imageIndex),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
screenshotReadbackBuffers.at(currentFrame).buffer,
|
||||
screenshotBuffer,
|
||||
1, ®ion);
|
||||
|
||||
addReadbackCallback([
|
||||
w = swapChainExtent.width,
|
||||
h = swapChainExtent.height,
|
||||
pendingScreenshotCallbacks = pendingScreenshotCallbacks,
|
||||
screenShotReadbackBuffer = screenshotReadbackBuffers.at(currentFrame),
|
||||
screenshotCallbackData = screenshotCallbackData]() {
|
||||
auto imageModule = Module::getInstance<love::image::Image>(M_IMAGE);
|
||||
|
||||
for (const auto &info : pendingScreenshotCallbacks)
|
||||
{
|
||||
image::ImageData *img = imageModule->newImageData(
|
||||
w,
|
||||
h,
|
||||
PIXELFORMAT_RGBA8_UNORM,
|
||||
screenShotReadbackBuffer.allocationInfo.pMappedData);
|
||||
info.callback(&info, img, screenshotCallbackData);
|
||||
img->release();
|
||||
}
|
||||
});
|
||||
|
||||
pendingScreenshotCallbacks.clear();
|
||||
Vulkan::cmdTransitionImageLayout(
|
||||
commandBuffers.at(currentFrame),
|
||||
swapChainImages.at(imageIndex),
|
||||
swapChainPixelFormat,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,7 +496,7 @@ void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallback
|
||||
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence) != VK_SUCCESS)
|
||||
throw love::Exception("failed to submit draw command buffer");
|
||||
|
||||
if (submitMode == SUBMIT_NOPRESENT || submitMode == SUBMIT_RESTART)
|
||||
if (submitMode == SUBMIT_NOPRESENT || submitMode == SUBMIT_RESTART || screenshotBuffer != VK_NULL_HANDLE)
|
||||
{
|
||||
vkQueueWaitIdle(graphicsQueue);
|
||||
|
||||
@@ -532,6 +507,64 @@ void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallback
|
||||
callbacks.clear();
|
||||
}
|
||||
|
||||
if (screenshotBuffer != VK_NULL_HANDLE)
|
||||
{
|
||||
auto imageModule = Module::getInstance<love::image::Image>(M_IMAGE);
|
||||
|
||||
for (int i = 0; i < (int)pendingScreenshotCallbacks.size(); i++)
|
||||
{
|
||||
const auto &info = pendingScreenshotCallbacks[i];
|
||||
image::ImageData *img = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
img = imageModule->newImageData(
|
||||
swapChainExtent.width,
|
||||
swapChainExtent.height,
|
||||
PIXELFORMAT_RGBA8_UNORM,
|
||||
screenshotAllocationInfo.pMappedData);
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
info.callback(&info, nullptr, nullptr);
|
||||
for (int j = i + 1; j < (int)pendingScreenshotCallbacks.size(); j++)
|
||||
{
|
||||
const auto& ninfo = pendingScreenshotCallbacks[j];
|
||||
ninfo.callback(&ninfo, nullptr, nullptr);
|
||||
}
|
||||
vmaDestroyBuffer(vmaAllocator, screenshotBuffer, screenshotAllocation);
|
||||
pendingScreenshotCallbacks.clear();
|
||||
throw;
|
||||
}
|
||||
|
||||
uint8 *screenshot = (uint8*)img->getData();
|
||||
|
||||
if (swapChainImageFormat == VK_FORMAT_B8G8R8A8_UNORM || swapChainImageFormat == VK_FORMAT_B8G8R8A8_SRGB)
|
||||
{
|
||||
// Convert from BGRA to RGBA and replace alpha with full opacity.
|
||||
for (size_t i = 0; i < img->getSize(); i += 4)
|
||||
{
|
||||
uint8 r = screenshot[i + 2];
|
||||
screenshot[i + 2] = screenshot[i + 0];
|
||||
screenshot[i + 0] = r;
|
||||
screenshot[i + 3] = 255;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replace alpha with full opacity.
|
||||
for (size_t i = 0; i < img->getSize(); i += 4)
|
||||
screenshot[i + 3] = 255;
|
||||
}
|
||||
|
||||
info.callback(&info, img, screenshotCallbackData);
|
||||
img->release();
|
||||
}
|
||||
|
||||
vmaDestroyBuffer(vmaAllocator, screenshotBuffer, screenshotAllocation);
|
||||
pendingScreenshotCallbacks.clear();
|
||||
}
|
||||
|
||||
if (submitMode == SUBMIT_RESTART)
|
||||
startRecordingGraphicsCommands();
|
||||
}
|
||||
@@ -637,7 +670,6 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
|
||||
|
||||
createSwapChain();
|
||||
createImageViews();
|
||||
createScreenshotCallbackBuffers();
|
||||
createColorResources();
|
||||
createDepthResources();
|
||||
transitionColorDepthLayouts = true;
|
||||
@@ -649,6 +681,9 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
|
||||
createSyncObjects();
|
||||
}
|
||||
|
||||
if (localUniformBuffer == nullptr)
|
||||
localUniformBuffer.set(new StreamBuffer(this, BUFFERUSAGE_UNIFORM, 1024 * 512 * 1), Acquire::NORETAIN);
|
||||
|
||||
beginFrame();
|
||||
|
||||
if (createBaseObjects)
|
||||
@@ -1057,6 +1092,22 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage)
|
||||
{
|
||||
format = getSizedFormat(format);
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case PIXELFORMAT_PVR1_RGB2_UNORM:
|
||||
case PIXELFORMAT_PVR1_RGB2_sRGB:
|
||||
case PIXELFORMAT_PVR1_RGB4_UNORM:
|
||||
case PIXELFORMAT_PVR1_RGB4_sRGB:
|
||||
case PIXELFORMAT_PVR1_RGBA2_UNORM:
|
||||
case PIXELFORMAT_PVR1_RGBA2_sRGB:
|
||||
case PIXELFORMAT_PVR1_RGBA4_UNORM:
|
||||
case PIXELFORMAT_PVR1_RGBA4_sRGB:
|
||||
// Lets not support these in Vulkan - they're deprecated.
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
auto vulkanFormat = Vulkan::getTextureFormat(format);
|
||||
|
||||
VkFormatProperties formatProperties;
|
||||
@@ -1188,12 +1239,6 @@ bool Graphics::dispatch(love::graphics::Shader *shader, love::graphics::Buffer *
|
||||
return true;
|
||||
}
|
||||
|
||||
Matrix4 Graphics::computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const
|
||||
{
|
||||
uint32 flags = DEVICE_PROJECTION_DEFAULT;
|
||||
return calculateDeviceProjection(projection, flags);
|
||||
}
|
||||
|
||||
void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture)
|
||||
{
|
||||
if (renderPassState.active)
|
||||
@@ -1277,6 +1322,7 @@ void Graphics::beginFrame()
|
||||
Vulkan::cmdTransitionImageLayout(
|
||||
commandBuffers.at(currentFrame),
|
||||
swapChainImages[imageIndex],
|
||||
swapChainPixelFormat,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
@@ -1286,6 +1332,7 @@ void Graphics::beginFrame()
|
||||
Vulkan::cmdTransitionImageLayout(
|
||||
commandBuffers.at(currentFrame),
|
||||
depthImage,
|
||||
depthStencilPixelFormat,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||
|
||||
@@ -1293,6 +1340,7 @@ void Graphics::beginFrame()
|
||||
Vulkan::cmdTransitionImageLayout(
|
||||
commandBuffers.at(currentFrame),
|
||||
colorImage,
|
||||
swapChainPixelFormat,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
@@ -1301,9 +1349,11 @@ void Graphics::beginFrame()
|
||||
|
||||
Vulkan::resetShaderSwitches();
|
||||
|
||||
for (const auto shader : usedShadersInFrame)
|
||||
for (const auto &shader : usedShadersInFrame)
|
||||
shader->newFrame();
|
||||
usedShadersInFrame.clear();
|
||||
|
||||
localUniformBuffer->nextFrame();
|
||||
}
|
||||
|
||||
void Graphics::startRecordingGraphicsCommands()
|
||||
@@ -1329,11 +1379,6 @@ void Graphics::endRecordingGraphicsCommands() {
|
||||
throw love::Exception("failed to record command buffer");
|
||||
}
|
||||
|
||||
const VkDeviceSize Graphics::getMinUniformBufferOffsetAlignment() const
|
||||
{
|
||||
return minUniformBufferOffsetAlignment;
|
||||
}
|
||||
|
||||
VkCommandBuffer Graphics::getCommandBufferForDataTransfer()
|
||||
{
|
||||
if (renderPassState.active)
|
||||
@@ -1379,8 +1424,22 @@ graphics::Shader::BuiltinUniformData Graphics::getCurrentBuiltinUniformData()
|
||||
// Same with point size.
|
||||
data.normalMatrix[1].w = getPointSize();
|
||||
|
||||
data.screenSizeParams.x = static_cast<float>(swapChainExtent.width);
|
||||
data.screenSizeParams.y = static_cast<float>(swapChainExtent.height);
|
||||
// Flip y to convert input y-up [-1, 1] to vulkan's y-down [-1, 1].
|
||||
// Convert input z [-1, 1] to vulkan [0, 1].
|
||||
uint32 flags = Shader::CLIP_TRANSFORM_FLIP_Y | Shader::CLIP_TRANSFORM_Z_NEG1_1_TO_0_1;
|
||||
data.clipSpaceParams = Shader::computeClipSpaceParams(flags);
|
||||
|
||||
const auto &rt = states.back().renderTargets.getFirstTarget();
|
||||
if (rt.texture != nullptr)
|
||||
{
|
||||
data.screenSizeParams.x = rt.texture->getPixelWidth(rt.mipmap);
|
||||
data.screenSizeParams.y = rt.texture->getPixelHeight(rt.mipmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.screenSizeParams.x = getPixelWidth();
|
||||
data.screenSizeParams.y = getPixelHeight();
|
||||
}
|
||||
|
||||
data.screenSizeParams.z = 1.0f;
|
||||
data.screenSizeParams.w = 0.0f;
|
||||
@@ -1459,6 +1518,18 @@ void Graphics::pickPhysicalDevice()
|
||||
deviceApiVersion = properties.apiVersion;
|
||||
|
||||
depthStencilFormat = findDepthFormat();
|
||||
switch (depthStencilFormat)
|
||||
{
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
depthStencilPixelFormat = PIXELFORMAT_DEPTH32_FLOAT_STENCIL8;
|
||||
break;
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
depthStencilPixelFormat = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
|
||||
break;
|
||||
default:
|
||||
throw love::Exception("Failed to convert vulkan depth/stencil swapchain pixel format %d to love PixelFormat.", depthStencilFormat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Graphics::checkDeviceExtensionSupport(VkPhysicalDevice device)
|
||||
@@ -1848,6 +1919,25 @@ void Graphics::createSwapChain()
|
||||
swapChainImageFormat = surfaceFormat.format;
|
||||
swapChainExtent = extent;
|
||||
preTransform = swapChainSupport.capabilities.currentTransform;
|
||||
|
||||
switch (swapChainImageFormat)
|
||||
{
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
swapChainPixelFormat = PIXELFORMAT_BGRA8_sRGB;
|
||||
break;
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
swapChainPixelFormat = PIXELFORMAT_BGRA8_UNORM;
|
||||
break;
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
swapChainPixelFormat = PIXELFORMAT_RGBA8_sRGB;
|
||||
break;
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
swapChainPixelFormat = PIXELFORMAT_RGBA8_UNORM;
|
||||
break;
|
||||
default:
|
||||
throw love::Exception("Failed to convert vulkan depth/stencil swapchain image format %d to love PixelFormat.", swapChainImageFormat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats)
|
||||
@@ -1983,65 +2073,6 @@ void Graphics::createImageViews()
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::createScreenshotCallbackBuffers()
|
||||
{
|
||||
screenshotReadbackBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
VkBufferCreateInfo bufferInfo{};
|
||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferInfo.size = 4ll * swapChainExtent.width * swapChainExtent.height;
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo{};
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
|
||||
auto result = vmaCreateBuffer(
|
||||
vmaAllocator,
|
||||
&bufferInfo,
|
||||
&allocCreateInfo,
|
||||
&screenshotReadbackBuffers.at(i).buffer,
|
||||
&screenshotReadbackBuffers.at(i).allocation,
|
||||
&screenshotReadbackBuffers.at(i).allocationInfo);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
throw love::Exception("failed to create screenshot readback buffer");
|
||||
|
||||
VkImageCreateInfo imageInfo{};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
|
||||
imageInfo.extent = {
|
||||
swapChainExtent.width,
|
||||
swapChainExtent.height,
|
||||
1
|
||||
};
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VmaAllocationCreateInfo imageAllocCreateInfo{};
|
||||
|
||||
result = vmaCreateImage(
|
||||
vmaAllocator,
|
||||
&imageInfo,
|
||||
&imageAllocCreateInfo,
|
||||
&screenshotReadbackBuffers.at(i).image,
|
||||
&screenshotReadbackBuffers.at(i).imageAllocation,
|
||||
nullptr);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
throw love::Exception("failed to create screenshot readback image");
|
||||
}
|
||||
}
|
||||
|
||||
VkFramebuffer Graphics::createFramebuffer(FramebufferConfiguration &configuration)
|
||||
{
|
||||
std::vector<VkImageView> attachments;
|
||||
@@ -2272,7 +2303,7 @@ void Graphics::createVulkanVertexFormat(
|
||||
attributeDescription.location = i;
|
||||
attributeDescription.binding = bufferBinding;
|
||||
attributeDescription.offset = attrib.offsetFromVertex;
|
||||
attributeDescription.format = Vulkan::getVulkanVertexFormat(attrib.format);
|
||||
attributeDescription.format = Vulkan::getVulkanVertexFormat(attrib.getFormat());
|
||||
|
||||
attributeDescriptions.push_back(attributeDescription);
|
||||
}
|
||||
@@ -2353,28 +2384,39 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi
|
||||
configuration.dynamicState.cullmode = cullmode;
|
||||
}
|
||||
|
||||
std::vector<VkBuffer> bufferVector;
|
||||
std::vector<VkDeviceSize> offsets;
|
||||
VkBuffer vkbuffers[VertexAttributes::MAX + 2];
|
||||
VkDeviceSize vkoffsets[VertexAttributes::MAX + 2];
|
||||
int buffercount = 0;
|
||||
|
||||
for (uint32_t i = 0; i < VertexAttributes::MAX; i++)
|
||||
uint32 allbits = buffers.useBits;
|
||||
uint32 i = 0;
|
||||
while (allbits)
|
||||
{
|
||||
if (buffers.useBits & (1u << i))
|
||||
uint32 bit = 1u << i;
|
||||
|
||||
if (buffers.useBits & bit)
|
||||
{
|
||||
bufferVector.push_back((VkBuffer)buffers.info[i].buffer->getHandle());
|
||||
offsets.push_back((VkDeviceSize)buffers.info[i].offset);
|
||||
vkbuffers[buffercount] = (VkBuffer)buffers.info[i].buffer->getHandle();
|
||||
vkoffsets[buffercount] = (VkDeviceSize)buffers.info[i].offset;
|
||||
buffercount++;
|
||||
}
|
||||
|
||||
i++;
|
||||
allbits >>= 1;
|
||||
}
|
||||
|
||||
if (!(attributes.enableBits & (1u << ATTRIB_TEXCOORD)))
|
||||
{
|
||||
bufferVector.push_back((VkBuffer)defaultConstantTexCoord->getHandle());
|
||||
offsets.push_back((VkDeviceSize)0);
|
||||
vkbuffers[buffercount] = (VkBuffer)defaultConstantTexCoord->getHandle();
|
||||
vkoffsets[buffercount] = (VkDeviceSize)0;
|
||||
buffercount++;
|
||||
}
|
||||
|
||||
if (!(attributes.enableBits & (1u << ATTRIB_COLOR)))
|
||||
{
|
||||
bufferVector.push_back((VkBuffer)defaultConstantColor->getHandle());
|
||||
offsets.push_back((VkDeviceSize)0);
|
||||
vkbuffers[buffercount] = (VkBuffer)defaultConstantColor->getHandle();
|
||||
vkoffsets[buffercount] = (VkDeviceSize)0;
|
||||
buffercount++;
|
||||
}
|
||||
|
||||
configuration.shader->setMainTex(texture);
|
||||
@@ -2382,7 +2424,9 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi
|
||||
ensureGraphicsPipelineConfiguration(configuration);
|
||||
|
||||
configuration.shader->cmdPushDescriptorSets(commandBuffers.at(currentFrame), VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
vkCmdBindVertexBuffers(commandBuffers.at(currentFrame), 0, static_cast<uint32_t>(bufferVector.size()), bufferVector.data(), offsets.data());
|
||||
|
||||
if (buffercount > 0)
|
||||
vkCmdBindVertexBuffers(commandBuffers.at(currentFrame), 0, static_cast<uint32_t>(buffercount), vkbuffers, vkoffsets);
|
||||
}
|
||||
|
||||
void Graphics::setDefaultRenderPass()
|
||||
@@ -2455,12 +2499,12 @@ void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, b
|
||||
|
||||
FramebufferConfiguration configuration{};
|
||||
|
||||
std::vector<VkImage> transitionImages;
|
||||
std::vector<std::tuple<VkImage, PixelFormat>> transitionImages;
|
||||
|
||||
for (const auto &color : rts.colors)
|
||||
{
|
||||
configuration.colorViews.push_back(dynamic_cast<Texture*>(color.texture)->getRenderTargetView(color.mipmap, color.slice));
|
||||
transitionImages.push_back((VkImage) color.texture->getHandle());
|
||||
transitionImages.push_back({ (VkImage)color.texture->getHandle(), color.texture->getPixelFormat() });
|
||||
}
|
||||
if (rts.depthStencil.texture != nullptr)
|
||||
configuration.staticData.depthView = dynamic_cast<Texture*>(rts.depthStencil.texture)->getRenderTargetView(rts.depthStencil.mipmap, rts.depthStencil.slice);
|
||||
@@ -2513,8 +2557,8 @@ void Graphics::startRenderPass()
|
||||
renderPassState.framebufferConfiguration.staticData.renderPass = renderPassState.beginInfo.renderPass;
|
||||
renderPassState.beginInfo.framebuffer = getFramebuffer(renderPassState.framebufferConfiguration);
|
||||
|
||||
for (const auto &image : renderPassState.transitionImages)
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
for (const auto &[image, format] : renderPassState.transitionImages)
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), image, format, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffers.at(currentFrame), &renderPassState.beginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
}
|
||||
@@ -2525,8 +2569,8 @@ void Graphics::endRenderPass()
|
||||
|
||||
vkCmdEndRenderPass(commandBuffers.at(currentFrame));
|
||||
|
||||
for (const auto &image : renderPassState.transitionImages)
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
for (const auto &[image, format] : renderPassState.transitionImages)
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), image, format, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
for (auto &colorAttachment : renderPassState.renderPassConfiguration.colorAttachments)
|
||||
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
@@ -2855,6 +2899,23 @@ int Graphics::getVsync() const
|
||||
return vsync;
|
||||
}
|
||||
|
||||
void Graphics::mapLocalUniformData(void *data, size_t size, VkDescriptorBufferInfo &bufferInfo)
|
||||
{
|
||||
size_t alignedSize = alignUp(size, minUniformBufferOffsetAlignment);
|
||||
|
||||
if (localUniformBuffer->getUsableSize() < alignedSize)
|
||||
localUniformBuffer.set(new StreamBuffer(this, BUFFERUSAGE_UNIFORM, localUniformBuffer->getSize() * 2), Acquire::NORETAIN);
|
||||
|
||||
auto mapInfo = localUniformBuffer->map(size);
|
||||
memcpy(mapInfo.data, data, size);
|
||||
|
||||
bufferInfo.buffer = (VkBuffer)localUniformBuffer->getHandle();
|
||||
bufferInfo.offset = localUniformBuffer->unmap(size);
|
||||
bufferInfo.range = size;
|
||||
|
||||
localUniformBuffer->markUsed(alignedSize);
|
||||
}
|
||||
|
||||
void Graphics::createColorResources()
|
||||
{
|
||||
if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
|
||||
@@ -3073,11 +3134,6 @@ void Graphics::cleanup()
|
||||
|
||||
void Graphics::cleanupSwapChain()
|
||||
{
|
||||
for (const auto &readbackBuffer : screenshotReadbackBuffers)
|
||||
{
|
||||
vmaDestroyBuffer(vmaAllocator, readbackBuffer.buffer, readbackBuffer.allocation);
|
||||
vmaDestroyImage(vmaAllocator, readbackBuffer.image, readbackBuffer.imageAllocation);
|
||||
}
|
||||
if (colorImage)
|
||||
{
|
||||
vkDestroyImageView(device, colorImageView, nullptr);
|
||||
@@ -3104,7 +3160,6 @@ void Graphics::recreateSwapChain()
|
||||
|
||||
createSwapChain();
|
||||
createImageViews();
|
||||
createScreenshotCallbackBuffers();
|
||||
createColorResources();
|
||||
createDepthResources();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -236,7 +236,7 @@ struct RenderpassState
|
||||
RenderPassConfiguration renderPassConfiguration{};
|
||||
FramebufferConfiguration framebufferConfiguration{};
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
std::vector<VkImage> transitionImages;
|
||||
std::vector<std::tuple<VkImage, PixelFormat>> transitionImages;
|
||||
uint32_t numColorAttachments = 0;
|
||||
float width = 0.0f;
|
||||
float height = 0.0f;
|
||||
@@ -249,16 +249,6 @@ struct RenderpassState
|
||||
OptionalInt mainWindowClearStencilValue;
|
||||
};
|
||||
|
||||
struct ScreenshotReadbackBuffer
|
||||
{
|
||||
VkBuffer buffer;
|
||||
VmaAllocation allocation;
|
||||
VmaAllocationInfo allocationInfo;
|
||||
|
||||
VkImage image;
|
||||
VmaAllocation imageAllocation;
|
||||
};
|
||||
|
||||
enum SubmitMode
|
||||
{
|
||||
SUBMIT_PRESENT,
|
||||
@@ -274,14 +264,13 @@ public:
|
||||
~Graphics();
|
||||
|
||||
// implementation for virtual functions
|
||||
const char *getName() const override;
|
||||
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;
|
||||
void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override;
|
||||
void clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth) override;
|
||||
Matrix4 computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const override;
|
||||
void discard(const std::vector<bool>& colorbuffers, bool depthstencil) override;
|
||||
void present(void *screenshotCallbackdata) override;
|
||||
void backbufferChanged(int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, bool backbufferdepth, int msaa) override;
|
||||
@@ -316,7 +305,6 @@ public:
|
||||
void queueCleanUp(std::function<void()> cleanUp);
|
||||
void addReadbackCallback(std::function<void()> callback);
|
||||
void submitGpuCommands(SubmitMode, void *screenshotCallbackData = nullptr);
|
||||
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
|
||||
VkSampler getCachedSampler(const SamplerState &sampler);
|
||||
void setComputeShader(Shader *computeShader);
|
||||
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
|
||||
@@ -325,6 +313,9 @@ public:
|
||||
VkSampleCountFlagBits getMsaaCount(int requestedMsaa) const;
|
||||
void setVsync(int vsync);
|
||||
int getVsync() const;
|
||||
void mapLocalUniformData(void *data, size_t size, VkDescriptorBufferInfo &bufferInfo);
|
||||
|
||||
uint32 getDeviceApiVersion() const { return deviceApiVersion; }
|
||||
|
||||
protected:
|
||||
graphics::ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
|
||||
@@ -353,7 +344,6 @@ private:
|
||||
VkCompositeAlphaFlagBitsKHR chooseCompositeAlpha(const VkSurfaceCapabilitiesKHR &capabilities);
|
||||
void createSwapChain();
|
||||
void createImageViews();
|
||||
void createScreenshotCallbackBuffers();
|
||||
VkFramebuffer createFramebuffer(FramebufferConfiguration &configuration);
|
||||
VkFramebuffer getFramebuffer(FramebufferConfiguration &configuration);
|
||||
void createDefaultShaders();
|
||||
@@ -406,7 +396,9 @@ private:
|
||||
Matrix4 displayRotation;
|
||||
std::vector<VkImage> swapChainImages;
|
||||
VkFormat swapChainImageFormat = VK_FORMAT_UNDEFINED;
|
||||
PixelFormat swapChainPixelFormat = PIXELFORMAT_UNKNOWN;
|
||||
VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED;
|
||||
PixelFormat depthStencilPixelFormat = PIXELFORMAT_UNKNOWN;
|
||||
VkExtent2D swapChainExtent = VkExtent2D();
|
||||
std::vector<VkImageView> swapChainImageViews;
|
||||
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
@@ -442,12 +434,12 @@ private:
|
||||
VmaAllocator vmaAllocator = VK_NULL_HANDLE;
|
||||
StrongRef<love::graphics::Buffer> defaultConstantColor;
|
||||
StrongRef<love::graphics::Buffer> defaultConstantTexCoord;
|
||||
StrongRef<StreamBuffer> localUniformBuffer;
|
||||
// functions that need to be called to cleanup objects that were needed for rendering a frame.
|
||||
// We need a vector for each frame in flight.
|
||||
std::vector<std::vector<std::function<void()>>> cleanUpFunctions;
|
||||
std::vector<std::vector<std::function<void()>>> readbackCallbacks;
|
||||
std::vector<ScreenshotReadbackBuffer> screenshotReadbackBuffers;
|
||||
std::set<Shader*> usedShadersInFrame;
|
||||
std::set<StrongRef<Shader>> usedShadersInFrame;
|
||||
RenderpassState renderPassState;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "graphics/vertex.h"
|
||||
#include "Shader.h"
|
||||
#include "Graphics.h"
|
||||
#include "common/Range.h"
|
||||
|
||||
#include "libraries/glslang/glslang/Public/ShaderLang.h"
|
||||
#include "libraries/glslang/glslang/Public/ResourceLimits.h"
|
||||
@@ -35,65 +36,65 @@ namespace graphics
|
||||
namespace vulkan
|
||||
{
|
||||
|
||||
static const uint32_t STREAMBUFFER_DEFAULT_SIZE = 16;
|
||||
static const uint32_t DESCRIPTOR_POOL_SIZE = 1000;
|
||||
|
||||
class BindingMapper
|
||||
{
|
||||
public:
|
||||
uint32_t operator()(spirv_cross::CompilerGLSL &comp, std::vector<uint32_t> &spirv, const std::string &name, const spirv_cross::ID &id)
|
||||
uint32_t operator()(spirv_cross::CompilerGLSL &comp, std::vector<uint32_t> &spirv, const std::string &name, int count, const spirv_cross::ID &id)
|
||||
{
|
||||
auto it = bindingMappings.find(name);
|
||||
if (it == bindingMappings.end())
|
||||
{
|
||||
auto binding = comp.get_decoration(id, spv::DecorationBinding);
|
||||
|
||||
if (isFreeBinding(binding))
|
||||
if (isFreeBinding(binding, count))
|
||||
{
|
||||
bindingMappings[name] = binding;
|
||||
bindingMappings[name] = Range(binding, count);
|
||||
return binding;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t freeBinding = getFreeBinding();
|
||||
uint32_t freeBinding = getFreeBinding(count);
|
||||
|
||||
uint32_t binaryBindingOffset;
|
||||
if (!comp.get_binary_offset_for_decoration(id, spv::DecorationBinding, binaryBindingOffset))
|
||||
throw love::Exception("could not get binary offset for binding");
|
||||
throw love::Exception("could not get binary offset for uniform %s binding", name.c_str());
|
||||
|
||||
spirv[binaryBindingOffset] = freeBinding;
|
||||
|
||||
bindingMappings[name] = freeBinding;
|
||||
bindingMappings[name] = Range(freeBinding, count);
|
||||
|
||||
return freeBinding;
|
||||
}
|
||||
}
|
||||
else
|
||||
return it->second;
|
||||
return (uint32_t)it->second.getOffset();
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
uint32_t getFreeBinding()
|
||||
uint32_t getFreeBinding(int count)
|
||||
{
|
||||
for (uint32_t i = 0;; i++)
|
||||
{
|
||||
if (isFreeBinding(i))
|
||||
if (isFreeBinding(i, count))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFreeBinding(uint32_t binding)
|
||||
bool isFreeBinding(uint32_t binding, int count)
|
||||
{
|
||||
Range r(binding, count);
|
||||
for (const auto &entry : bindingMappings)
|
||||
{
|
||||
if (entry.second == binding)
|
||||
if (entry.second.intersects(r))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<std::string, uint32_t> bindingMappings;
|
||||
std::map<std::string, Range> bindingMappings;
|
||||
|
||||
};
|
||||
|
||||
@@ -155,14 +156,11 @@ bool Shader::loadVolatile()
|
||||
builtinUniformInfo[i] = nullptr;
|
||||
|
||||
compileShaders();
|
||||
calculateUniformBufferSizeAligned();
|
||||
createDescriptorSetLayout();
|
||||
createPipelineLayout();
|
||||
createDescriptorPoolSizes();
|
||||
createStreamBuffers();
|
||||
descriptorPools.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
currentFrame = 0;
|
||||
currentUsedUniformStreamBuffersCount = 0;
|
||||
newFrame();
|
||||
|
||||
return true;
|
||||
@@ -173,31 +171,6 @@ void Shader::unloadVolatile()
|
||||
if (shaderModules.empty())
|
||||
return;
|
||||
|
||||
for (const auto &uniform : uniformInfos)
|
||||
{
|
||||
switch (uniform.second.baseType)
|
||||
{
|
||||
case UNIFORM_SAMPLER:
|
||||
case UNIFORM_STORAGETEXTURE:
|
||||
for (int i = 0; i < uniform.second.count; i++)
|
||||
{
|
||||
if (uniform.second.textures[i] != nullptr)
|
||||
uniform.second.textures[i]->release();
|
||||
}
|
||||
delete[] uniform.second.textures;
|
||||
break;
|
||||
case UNIFORM_TEXELBUFFER:
|
||||
case UNIFORM_STORAGEBUFFER:
|
||||
for (int i = 0; i < uniform.second.count; i++)
|
||||
{
|
||||
if (uniform.second.buffers[i] != nullptr)
|
||||
uniform.second.buffers[i]->release();
|
||||
}
|
||||
delete[] uniform.second.buffers;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vgfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout, descriptorPools = descriptorPools, computePipeline = computePipeline](){
|
||||
for (const auto &pools : descriptorPools)
|
||||
{
|
||||
@@ -212,12 +185,8 @@ void Shader::unloadVolatile()
|
||||
vkDestroyPipeline(device, computePipeline, nullptr);
|
||||
});
|
||||
|
||||
for (const auto streamBuffer : streamBuffers)
|
||||
streamBuffer->release();
|
||||
|
||||
shaderModules.clear();
|
||||
shaderStages.clear();
|
||||
streamBuffers.clear();
|
||||
descriptorPools.clear();
|
||||
}
|
||||
|
||||
@@ -240,51 +209,20 @@ void Shader::newFrame()
|
||||
{
|
||||
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
|
||||
currentUsedUniformStreamBuffersCount = 0;
|
||||
currentDescriptorPool = 0;
|
||||
|
||||
if (streamBuffers.size() > 1)
|
||||
{
|
||||
size_t newSize = 0;
|
||||
for (auto streamBuffer : streamBuffers)
|
||||
{
|
||||
newSize += streamBuffer->getSize();
|
||||
streamBuffer->release();
|
||||
}
|
||||
streamBuffers.clear();
|
||||
streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, newSize));
|
||||
}
|
||||
else
|
||||
streamBuffers.at(0)->nextFrame();
|
||||
|
||||
for (VkDescriptorPool pool : descriptorPools[currentFrame])
|
||||
vkResetDescriptorPool(device, pool, 0);
|
||||
}
|
||||
|
||||
void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint bindPoint)
|
||||
{
|
||||
VkDescriptorSet currentDescriptorSet = allocateDescriptorSet();
|
||||
|
||||
std::vector<VkDescriptorBufferInfo> bufferInfos;
|
||||
bufferInfos.reserve(numBuffers);
|
||||
|
||||
std::vector<VkDescriptorImageInfo> imageInfos;
|
||||
imageInfos.reserve(numTextures);
|
||||
|
||||
std::vector<VkBufferView> bufferViews;
|
||||
bufferViews.reserve(numBufferViews);
|
||||
|
||||
std::vector<VkWriteDescriptorSet> descriptorWrites;
|
||||
int imageIndex = 0;
|
||||
int bufferIndex = 0;
|
||||
int bufferViewIndex = 0;
|
||||
|
||||
if (!localUniformData.empty())
|
||||
{
|
||||
auto usedStreamBufferMemory = currentUsedUniformStreamBuffersCount * uniformBufferSizeAligned;
|
||||
if (usedStreamBufferMemory >= streamBuffers.back()->getSize())
|
||||
{
|
||||
streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned));
|
||||
currentUsedUniformStreamBuffersCount = 0;
|
||||
}
|
||||
|
||||
if (builtinUniformDataOffset.hasValue)
|
||||
{
|
||||
auto builtinData = vgfx->getCurrentBuiltinUniformData();
|
||||
@@ -292,127 +230,93 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
|
||||
memcpy(dst, &builtinData, sizeof(builtinData));
|
||||
}
|
||||
|
||||
auto currentStreamBuffer = streamBuffers.back();
|
||||
|
||||
auto mapInfo = currentStreamBuffer->map(uniformBufferSizeAligned);
|
||||
memcpy(mapInfo.data, localUniformData.data(), localUniformData.size());
|
||||
auto offset = currentStreamBuffer->unmap(uniformBufferSizeAligned);
|
||||
currentStreamBuffer->markUsed(uniformBufferSizeAligned);
|
||||
|
||||
VkDescriptorBufferInfo bufferInfo{};
|
||||
bufferInfo.buffer = (VkBuffer)currentStreamBuffer->getHandle();
|
||||
bufferInfo.offset = offset;
|
||||
bufferInfo.range = localUniformData.size();
|
||||
|
||||
bufferInfos.push_back(bufferInfo);
|
||||
|
||||
VkWriteDescriptorSet uniformWrite{};
|
||||
uniformWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
uniformWrite.dstSet = currentDescriptorSet;
|
||||
uniformWrite.dstBinding = localUniformLocation;
|
||||
uniformWrite.dstArrayElement = 0;
|
||||
uniformWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
uniformWrite.descriptorCount = 1;
|
||||
uniformWrite.pBufferInfo = &bufferInfos[bufferInfos.size() - 1];
|
||||
|
||||
descriptorWrites.push_back(uniformWrite);
|
||||
|
||||
currentUsedUniformStreamBuffersCount++;
|
||||
vgfx->mapLocalUniformData(localUniformData.data(), localUniformData.size(), descriptorBuffers[bufferIndex++]);
|
||||
}
|
||||
|
||||
for (const auto &u : uniformInfos)
|
||||
// TODO: iteration order must match the order at the end of compileShaders right now.
|
||||
// TODO: We can store data via setTextures and setBuffers instead of iterating over
|
||||
// everything here.
|
||||
for (const auto &u : reflection.sampledTextures)
|
||||
{
|
||||
auto &info = u.second;
|
||||
|
||||
if (usesLocalUniformData(&info))
|
||||
const auto &info = u.second;
|
||||
if (!info.active)
|
||||
continue;
|
||||
|
||||
if (info.baseType == UNIFORM_SAMPLER || info.baseType == UNIFORM_STORAGETEXTURE)
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
bool isSampler = info.baseType == UNIFORM_SAMPLER;
|
||||
auto vkTexture = dynamic_cast<Texture*>(activeTextures[info.resourceIndex + i]);
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
auto vkTexture = dynamic_cast<Texture*>(info.textures[i]);
|
||||
if (vkTexture == nullptr)
|
||||
throw love::Exception("uniform variable %s is not set.", info.name.c_str());
|
||||
|
||||
if (vkTexture == nullptr)
|
||||
throw love::Exception("uniform variable %s is not set.", info.name.c_str());
|
||||
VkDescriptorImageInfo &imageInfo = descriptorImages[imageIndex++];
|
||||
|
||||
VkDescriptorImageInfo imageInfo{};
|
||||
|
||||
imageInfo.imageLayout = vkTexture->getImageLayout();
|
||||
imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
|
||||
if (isSampler)
|
||||
imageInfo.sampler = (VkSampler)vkTexture->getSamplerHandle();
|
||||
|
||||
imageInfos.push_back(imageInfo);
|
||||
}
|
||||
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = currentDescriptorSet;
|
||||
write.dstBinding = info.location;
|
||||
write.dstArrayElement = 0;
|
||||
if (isSampler)
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
else
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
write.descriptorCount = static_cast<uint32_t>(info.count);
|
||||
write.pImageInfo = &imageInfos[imageInfos.size() - info.count];
|
||||
|
||||
descriptorWrites.push_back(write);
|
||||
}
|
||||
if (info.baseType == UNIFORM_STORAGEBUFFER)
|
||||
{
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = currentDescriptorSet;
|
||||
write.dstBinding = info.location;
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
write.descriptorCount = info.count;
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
if (info.buffers[i] == nullptr)
|
||||
throw love::Exception("uniform variable %s is not set.", info.name.c_str());
|
||||
|
||||
VkDescriptorBufferInfo bufferInfo{};
|
||||
bufferInfo.buffer = (VkBuffer)info.buffers[i]->getHandle();;
|
||||
bufferInfo.offset = 0;
|
||||
bufferInfo.range = info.buffers[i]->getSize();
|
||||
|
||||
bufferInfos.push_back(bufferInfo);
|
||||
}
|
||||
|
||||
write.pBufferInfo = &bufferInfos[bufferInfos.size() - info.count];
|
||||
|
||||
descriptorWrites.push_back(write);
|
||||
}
|
||||
if (info.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = currentDescriptorSet;
|
||||
write.dstBinding = info.location;
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
||||
write.descriptorCount = info.count;
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
if (info.buffers[i] == nullptr)
|
||||
throw love::Exception("uniform variable %s is not set.", info.name.c_str());
|
||||
|
||||
bufferViews.push_back((VkBufferView)info.buffers[i]->getTexelBufferHandle());
|
||||
}
|
||||
|
||||
write.pTexelBufferView = &bufferViews[bufferViews.size() - info.count];
|
||||
|
||||
descriptorWrites.push_back(write);
|
||||
imageInfo.imageLayout = vkTexture->getImageLayout();
|
||||
imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
|
||||
imageInfo.sampler = (VkSampler)vkTexture->getSamplerHandle();
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &u : reflection.storageTextures)
|
||||
{
|
||||
const auto &info = u.second;
|
||||
if (!info.active)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
auto vkTexture = dynamic_cast<Texture*>(activeTextures[info.resourceIndex + i]);
|
||||
|
||||
if (vkTexture == nullptr)
|
||||
throw love::Exception("uniform variable %s is not set.", info.name.c_str());
|
||||
|
||||
VkDescriptorImageInfo &imageInfo = descriptorImages[imageIndex++];
|
||||
|
||||
imageInfo.imageLayout = vkTexture->getImageLayout();
|
||||
imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &u : reflection.texelBuffers)
|
||||
{
|
||||
const auto &info = u.second;
|
||||
if (!info.active)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
auto b = activeBuffers[info.resourceIndex + i];
|
||||
if (b == nullptr)
|
||||
throw love::Exception("uniform variable %s is not set.", info.name.c_str());
|
||||
|
||||
descriptorBufferViews[bufferViewIndex++] = (VkBufferView)b->getTexelBufferHandle();
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &u : reflection.storageBuffers)
|
||||
{
|
||||
const auto &info = u.second;
|
||||
if (!info.active)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
auto b = activeBuffers[info.resourceIndex + i];
|
||||
if (b == nullptr)
|
||||
throw love::Exception("uniform variable %s is not set.", info.name.c_str());
|
||||
|
||||
VkDescriptorBufferInfo &bufferInfo = descriptorBuffers[bufferIndex++];
|
||||
bufferInfo.buffer = (VkBuffer)b->getHandle();
|
||||
bufferInfo.offset = 0;
|
||||
bufferInfo.range = b->getSize();
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorSet currentDescriptorSet = allocateDescriptorSet();
|
||||
|
||||
for (auto &write : descriptorWrites)
|
||||
write.dstSet = currentDescriptorSet;
|
||||
|
||||
vkUpdateDescriptorSets(device, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr);
|
||||
|
||||
vkCmdBindDescriptorSets(commandBuffer, bindPoint, pipelineLayout, 0, 1, ¤tDescriptorSet, 0, nullptr);
|
||||
@@ -444,12 +348,6 @@ int Shader::getVertexAttributeIndex(const std::string &name)
|
||||
return it == attributes.end() ? -1 : it->second;
|
||||
}
|
||||
|
||||
const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
|
||||
{
|
||||
const auto it = uniformInfos.find(name);
|
||||
return it != uniformInfos.end() ? &(it->second) : nullptr;
|
||||
}
|
||||
|
||||
const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const
|
||||
{
|
||||
return builtinUniformInfo[builtin];
|
||||
@@ -466,11 +364,15 @@ void Shader::updateUniform(const UniformInfo *info, int count)
|
||||
|
||||
void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures, int count)
|
||||
{
|
||||
if (current == this)
|
||||
Graphics::flushBatchedDrawsGlobal();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
auto oldTexture = info->textures[i];
|
||||
info->textures[i] = textures[i];
|
||||
info->textures[i]->retain();
|
||||
int resourceindex = info->resourceIndex + i;
|
||||
auto oldTexture = activeTextures[resourceindex];
|
||||
activeTextures[resourceindex] = textures[i];
|
||||
activeTextures[resourceindex]->retain();
|
||||
if (oldTexture)
|
||||
oldTexture->release();
|
||||
}
|
||||
@@ -478,24 +380,20 @@ void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures,
|
||||
|
||||
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
|
||||
{
|
||||
if (current == this)
|
||||
Graphics::flushBatchedDrawsGlobal();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
auto oldBuffer = info->buffers[i];
|
||||
info->buffers[i] = buffers[i];
|
||||
info->buffers[i]->retain();
|
||||
int resourceindex = info->resourceIndex + i;
|
||||
auto oldBuffer = activeBuffers[resourceindex];
|
||||
activeBuffers[resourceindex] = buffers[i];
|
||||
activeBuffers[resourceindex]->retain();
|
||||
if (oldBuffer)
|
||||
oldBuffer->release();
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::calculateUniformBufferSizeAligned()
|
||||
{
|
||||
auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
|
||||
size_t size = localUniformStagingData.size();
|
||||
auto factor = static_cast<VkDeviceSize>(std::ceil(static_cast<float>(size) / static_cast<float>(minAlignment)));
|
||||
uniformBufferSizeAligned = factor * minAlignment;
|
||||
}
|
||||
|
||||
void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename)
|
||||
{
|
||||
using namespace spirv_cross;
|
||||
@@ -524,38 +422,25 @@ void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::
|
||||
continue;
|
||||
}
|
||||
|
||||
UniformInfo u{};
|
||||
u.name = name;
|
||||
name = canonicaliizeUniformName(name);
|
||||
|
||||
auto uniformit = reflection.allUniforms.find(name);
|
||||
if (uniformit == reflection.allUniforms.end())
|
||||
{
|
||||
handleUnknownUniformName(name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
UniformInfo &u = *(uniformit->second);
|
||||
|
||||
u.active = true;
|
||||
u.dataSize = memberSize;
|
||||
u.count = memberType.array.empty() ? 1 : memberType.array[0];
|
||||
u.components = 1;
|
||||
u.data = localUniformStagingData.data() + offset;
|
||||
|
||||
if (memberType.columns == 1)
|
||||
const auto &valuesit = reflection.localUniformInitializerValues.find(name);
|
||||
if (valuesit != reflection.localUniformInitializerValues.end())
|
||||
{
|
||||
if (memberType.basetype == SPIRType::Int)
|
||||
u.baseType = UNIFORM_INT;
|
||||
else if (memberType.basetype == SPIRType::UInt)
|
||||
u.baseType = UNIFORM_UINT;
|
||||
else
|
||||
u.baseType = UNIFORM_FLOAT;
|
||||
u.components = memberType.vecsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
u.baseType = UNIFORM_MATRIX;
|
||||
u.matrix.rows = memberType.vecsize;
|
||||
u.matrix.columns = memberType.columns;
|
||||
}
|
||||
|
||||
const auto &reflectionIt = validationReflection.localUniforms.find(u.name);
|
||||
if (reflectionIt != validationReflection.localUniforms.end())
|
||||
{
|
||||
const auto &localUniform = reflectionIt->second;
|
||||
if (localUniform.dataType == DATA_BASETYPE_BOOL)
|
||||
u.baseType = UNIFORM_BOOL;
|
||||
|
||||
const auto &values = localUniform.initializerValues;
|
||||
const auto &values = valuesit->second;
|
||||
if (!values.empty())
|
||||
memcpy(
|
||||
u.data,
|
||||
@@ -563,14 +448,12 @@ void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::
|
||||
std::min(u.dataSize, values.size() * sizeof(LocalUniformValue)));
|
||||
}
|
||||
|
||||
uniformInfos[u.name] = u;
|
||||
|
||||
BuiltinUniform builtin = BUILTIN_MAX_ENUM;
|
||||
if (getConstant(u.name.c_str(), builtin))
|
||||
{
|
||||
if (builtin == BUILTIN_UNIFORMS_PER_DRAW)
|
||||
builtinUniformDataOffset = offset;
|
||||
builtinUniformInfo[builtin] = &uniformInfos[u.name];
|
||||
builtinUniformInfo[builtin] = &u;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -621,7 +504,7 @@ void Shader::compileShaders()
|
||||
bool forceDefault = false;
|
||||
bool forwardCompat = true;
|
||||
|
||||
if (!tshader->parse(GetDefaultResources(), defaultVersion, defaultProfile, forceDefault, forwardCompat, EShMsgSuppressWarnings))
|
||||
if (!tshader->parse(GetResources(), defaultVersion, defaultProfile, forceDefault, forwardCompat, EShMsgSuppressWarnings))
|
||||
{
|
||||
const char *stageName = "unknown";
|
||||
ShaderStage::getConstant(stage, stageName);
|
||||
@@ -643,8 +526,6 @@ void Shader::compileShaders()
|
||||
if (!program->mapIO())
|
||||
throw love::Exception("mapIO failed");
|
||||
|
||||
uniformInfos.clear();
|
||||
|
||||
BindingMapper bindingMapper;
|
||||
|
||||
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
|
||||
@@ -679,7 +560,7 @@ void Shader::compileShaders()
|
||||
|
||||
localUniformStagingData.resize(defaultUniformBlockSize);
|
||||
localUniformData.resize(defaultUniformBlockSize);
|
||||
localUniformLocation = bindingMapper(comp, spirv, resource.name, resource.id);
|
||||
localUniformLocation = bindingMapper(comp, spirv, resource.name, 1, resource.id);
|
||||
|
||||
memset(localUniformStagingData.data(), 0, defaultUniformBlockSize);
|
||||
memset(localUniformData.data(), 0, defaultUniformBlockSize);
|
||||
@@ -695,119 +576,51 @@ void Shader::compileShaders()
|
||||
|
||||
for (const auto &r : shaderResources.sampled_images)
|
||||
{
|
||||
const SPIRType &basetype = comp.get_type(r.base_type_id);
|
||||
const SPIRType &type = comp.get_type(r.type_id);
|
||||
const SPIRType &imagetype = comp.get_type(basetype.image.type);
|
||||
|
||||
graphics::Shader::UniformInfo info;
|
||||
info.location = bindingMapper(comp, spirv, r.name, r.id);
|
||||
info.baseType = UNIFORM_SAMPLER;
|
||||
info.name = r.name;
|
||||
info.count = type.array.empty() ? 1 : type.array[0];
|
||||
info.isDepthSampler = type.image.depth;
|
||||
info.components = 1;
|
||||
|
||||
switch (imagetype.basetype)
|
||||
std::string name = canonicaliizeUniformName(r.name);
|
||||
auto uniformit = reflection.allUniforms.find(name);
|
||||
if (uniformit == reflection.allUniforms.end())
|
||||
{
|
||||
case SPIRType::Float:
|
||||
info.dataBaseType = DATA_BASETYPE_FLOAT;
|
||||
break;
|
||||
case SPIRType::Int:
|
||||
info.dataBaseType = DATA_BASETYPE_INT;
|
||||
break;
|
||||
case SPIRType::UInt:
|
||||
info.dataBaseType = DATA_BASETYPE_UINT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
handleUnknownUniformName(name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (basetype.image.dim)
|
||||
{
|
||||
case spv::Dim2D:
|
||||
info.textureType = basetype.image.arrayed ? TEXTURE_2D_ARRAY : TEXTURE_2D;
|
||||
info.textures = new love::graphics::Texture *[info.count];
|
||||
break;
|
||||
case spv::Dim3D:
|
||||
info.textureType = TEXTURE_VOLUME;
|
||||
info.textures = new love::graphics::Texture *[info.count];
|
||||
break;
|
||||
case spv::DimCube:
|
||||
if (basetype.image.arrayed) {
|
||||
throw love::Exception("cubemap arrays are not currently supported");
|
||||
}
|
||||
info.textureType = TEXTURE_CUBE;
|
||||
info.textures = new love::graphics::Texture *[info.count];
|
||||
break;
|
||||
case spv::DimBuffer:
|
||||
info.baseType = UNIFORM_TEXELBUFFER;
|
||||
info.buffers = new love::graphics::Buffer *[info.count];
|
||||
break;
|
||||
default:
|
||||
throw love::Exception("unknown dim");
|
||||
}
|
||||
UniformInfo &u = *(uniformit->second);
|
||||
u.active = true;
|
||||
u.location = bindingMapper(comp, spirv, name, u.count, r.id);
|
||||
|
||||
if (info.baseType == UNIFORM_TEXELBUFFER)
|
||||
{
|
||||
for (int i = 0; i < info.count; i++)
|
||||
info.buffers[i] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
info.textures[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uniformInfos[r.name] = info;
|
||||
BuiltinUniform builtin;
|
||||
if (getConstant(r.name.c_str(), builtin))
|
||||
builtinUniformInfo[builtin] = &uniformInfos[info.name];
|
||||
if (getConstant(name.c_str(), builtin))
|
||||
builtinUniformInfo[builtin] = &u;
|
||||
}
|
||||
|
||||
for (const auto &r : shaderResources.storage_buffers)
|
||||
{
|
||||
const auto &type = comp.get_type(r.type_id);
|
||||
|
||||
UniformInfo u{};
|
||||
u.baseType = UNIFORM_STORAGEBUFFER;
|
||||
u.components = 1;
|
||||
u.name = r.name;
|
||||
u.count = type.array.empty() ? 1 : type.array[0];
|
||||
|
||||
if (!fillUniformReflectionData(u))
|
||||
std::string name = canonicaliizeUniformName(r.name);
|
||||
const auto &uniformit = reflection.storageBuffers.find(name);
|
||||
if (uniformit == reflection.storageBuffers.end())
|
||||
{
|
||||
handleUnknownUniformName(name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
u.location = bindingMapper(comp, spirv, r.name, r.id);
|
||||
u.buffers = new love::graphics::Buffer *[u.count];
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.buffers[i] = nullptr;
|
||||
|
||||
uniformInfos[u.name] = u;
|
||||
UniformInfo &u = uniformit->second;
|
||||
u.active = true;
|
||||
u.location = bindingMapper(comp, spirv, name, u.count, r.id);
|
||||
}
|
||||
|
||||
for (const auto &r : shaderResources.storage_images)
|
||||
{
|
||||
const auto &type = comp.get_type(r.type_id);
|
||||
|
||||
UniformInfo u{};
|
||||
u.baseType = UNIFORM_STORAGETEXTURE;
|
||||
u.components = 1;
|
||||
u.name = r.name;
|
||||
u.count = type.array.empty() ? 1 : type.array[0];
|
||||
|
||||
if (!fillUniformReflectionData(u))
|
||||
std::string name = canonicaliizeUniformName(r.name);
|
||||
const auto &uniformit = reflection.storageTextures.find(name);
|
||||
if (uniformit == reflection.storageTextures.end())
|
||||
{
|
||||
handleUnknownUniformName(name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
u.textures = new love::graphics::Texture *[u.count];
|
||||
u.location = bindingMapper(comp, spirv, r.name, r.id);
|
||||
|
||||
for (int i = 0; i < u.count; i++)
|
||||
u.textures[i] = nullptr;
|
||||
|
||||
uniformInfos[u.name] = u;
|
||||
UniformInfo &u = uniformit->second;
|
||||
u.active = true;
|
||||
u.location = bindingMapper(comp, spirv, name, u.count, r.id);
|
||||
}
|
||||
|
||||
if (shaderStage == SHADERSTAGE_VERTEX)
|
||||
@@ -826,7 +639,7 @@ void Shader::compileShaders()
|
||||
|
||||
uint32_t locationOffset;
|
||||
if (!comp.get_binary_offset_for_decoration(r.id, spv::DecorationLocation, locationOffset))
|
||||
throw love::Exception("could not get binary offset for location");
|
||||
throw love::Exception("could not get binary offset for vertex attribute %s location", r.name.c_str());
|
||||
|
||||
spirv[locationOffset] = (uint32_t)index;
|
||||
|
||||
@@ -868,31 +681,151 @@ void Shader::compileShaders()
|
||||
shaderStages.push_back(shaderStageInfo);
|
||||
}
|
||||
|
||||
numBuffers = 0;
|
||||
numTextures = 0;
|
||||
numBufferViews = 0;
|
||||
int numBuffers = 0;
|
||||
int numTextures = 0;
|
||||
int numBufferViews = 0;
|
||||
|
||||
if (localUniformData.size() > 0)
|
||||
numBuffers++;
|
||||
|
||||
for (const auto &u : uniformInfos)
|
||||
for (const auto kvp : reflection.allUniforms)
|
||||
{
|
||||
switch (u.second.baseType)
|
||||
if (!kvp.second->active)
|
||||
continue;
|
||||
|
||||
switch (kvp.second->baseType)
|
||||
{
|
||||
case UNIFORM_SAMPLER:
|
||||
case UNIFORM_STORAGETEXTURE:
|
||||
numTextures++;
|
||||
numTextures += kvp.second->count;
|
||||
break;
|
||||
case UNIFORM_STORAGEBUFFER:
|
||||
numBuffers++;
|
||||
numBuffers += kvp.second->count;
|
||||
break;
|
||||
case UNIFORM_TEXELBUFFER:
|
||||
numBufferViews++;
|
||||
numBufferViews += kvp.second->count;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
descriptorWrites.clear();
|
||||
|
||||
descriptorBuffers.clear();
|
||||
descriptorBuffers.reserve(numBuffers);
|
||||
|
||||
descriptorImages.clear();
|
||||
descriptorImages.reserve(numTextures);
|
||||
|
||||
descriptorBufferViews.clear();
|
||||
descriptorBufferViews.reserve(numBufferViews);
|
||||
|
||||
if (localUniformData.size() > 0)
|
||||
{
|
||||
VkDescriptorBufferInfo bufferInfo{};
|
||||
bufferInfo.range = localUniformData.size();
|
||||
|
||||
descriptorBuffers.push_back(bufferInfo);
|
||||
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstBinding = localUniformLocation;
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
write.descriptorCount = 1;
|
||||
write.pBufferInfo = &descriptorBuffers.back();
|
||||
descriptorWrites.push_back(write);
|
||||
}
|
||||
|
||||
for (const auto &u : reflection.sampledTextures)
|
||||
{
|
||||
const UniformInfo &info = u.second;
|
||||
if (!info.active)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
VkDescriptorImageInfo imageInfo{};
|
||||
descriptorImages.push_back(imageInfo);
|
||||
}
|
||||
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstBinding = info.location;
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
write.descriptorCount = static_cast<uint32_t>(info.count);
|
||||
write.pImageInfo = &descriptorImages[descriptorImages.size() - info.count];
|
||||
|
||||
descriptorWrites.push_back(write);
|
||||
}
|
||||
|
||||
for (const auto &u : reflection.storageTextures)
|
||||
{
|
||||
const UniformInfo &info = u.second;
|
||||
if (!info.active)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
VkDescriptorImageInfo imageInfo{};
|
||||
descriptorImages.push_back(imageInfo);
|
||||
}
|
||||
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstBinding = info.location;
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
write.descriptorCount = static_cast<uint32_t>(info.count);
|
||||
write.pImageInfo = &descriptorImages[descriptorImages.size() - info.count];
|
||||
|
||||
descriptorWrites.push_back(write);
|
||||
}
|
||||
|
||||
for (const auto &u : reflection.texelBuffers)
|
||||
{
|
||||
const UniformInfo &info = u.second;
|
||||
if (!info.active)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
descriptorBufferViews.push_back(VK_NULL_HANDLE);
|
||||
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstBinding = info.location;
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
||||
write.descriptorCount = info.count;
|
||||
write.pTexelBufferView = &descriptorBufferViews[descriptorBufferViews.size() - info.count];
|
||||
|
||||
descriptorWrites.push_back(write);
|
||||
}
|
||||
|
||||
for (const auto &u : reflection.storageBuffers)
|
||||
{
|
||||
const UniformInfo &info = u.second;
|
||||
if (!info.active)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < info.count; i++)
|
||||
{
|
||||
VkDescriptorBufferInfo bufferInfo{};
|
||||
descriptorBuffers.push_back(bufferInfo);
|
||||
}
|
||||
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstBinding = info.location;
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
write.descriptorCount = info.count;
|
||||
write.pBufferInfo = &descriptorBuffers[descriptorBuffers.size() - info.count];
|
||||
|
||||
descriptorWrites.push_back(write);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::createDescriptorSetLayout()
|
||||
@@ -905,16 +838,19 @@ void Shader::createDescriptorSetLayout()
|
||||
else
|
||||
stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
||||
for (auto const &entry : uniformInfos)
|
||||
for (auto const &entry : reflection.allUniforms)
|
||||
{
|
||||
auto type = Vulkan::getDescriptorType(entry.second.baseType);
|
||||
if (!entry.second->active)
|
||||
continue;
|
||||
|
||||
auto type = Vulkan::getDescriptorType(entry.second->baseType);
|
||||
if (type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||
{
|
||||
VkDescriptorSetLayoutBinding layoutBinding{};
|
||||
|
||||
layoutBinding.binding = entry.second.location;
|
||||
layoutBinding.binding = entry.second->location;
|
||||
layoutBinding.descriptorType = type;
|
||||
layoutBinding.descriptorCount = entry.second.count;
|
||||
layoutBinding.descriptorCount = entry.second->count;
|
||||
layoutBinding.stageFlags = stageFlags;
|
||||
|
||||
bindings.push_back(layoutBinding);
|
||||
@@ -976,10 +912,13 @@ void Shader::createDescriptorPoolSizes()
|
||||
descriptorPoolSizes.push_back(size);
|
||||
}
|
||||
|
||||
for (const auto &entry : uniformInfos)
|
||||
for (const auto &entry : reflection.allUniforms)
|
||||
{
|
||||
if (entry.second->location < 0)
|
||||
continue;
|
||||
|
||||
VkDescriptorPoolSize size{};
|
||||
auto type = Vulkan::getDescriptorType(entry.second.baseType);
|
||||
auto type = Vulkan::getDescriptorType(entry.second->baseType);
|
||||
if (type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||
continue;
|
||||
|
||||
@@ -989,13 +928,6 @@ void Shader::createDescriptorPoolSizes()
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::createStreamBuffers()
|
||||
{
|
||||
size_t size = STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned;
|
||||
if (size > 0)
|
||||
streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, size));
|
||||
}
|
||||
|
||||
void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture)
|
||||
{
|
||||
std::array<graphics::Texture*, 3> textures = {
|
||||
@@ -1012,29 +944,26 @@ void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cb
|
||||
|
||||
for (size_t i = 0; i < textures.size(); i++)
|
||||
{
|
||||
if (builtinUniformInfo[builtIns[i]] != nullptr)
|
||||
const UniformInfo *u = builtinUniformInfo[builtIns[i]];
|
||||
if (u != nullptr)
|
||||
{
|
||||
textures[i]->retain();
|
||||
if (builtinUniformInfo[builtIns[i]]->textures[0])
|
||||
builtinUniformInfo[builtIns[i]]->textures[0]->release();
|
||||
builtinUniformInfo[builtIns[i]]->textures[0] = textures[i];
|
||||
if (activeTextures[u->resourceIndex])
|
||||
activeTextures[u->resourceIndex]->release();
|
||||
activeTextures[u->resourceIndex] = textures[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::hasUniform(const std::string &name) const
|
||||
{
|
||||
return uniformInfos.find(name) != uniformInfos.end();
|
||||
}
|
||||
|
||||
void Shader::setMainTex(graphics::Texture *texture)
|
||||
{
|
||||
if (builtinUniformInfo[BUILTIN_TEXTURE_MAIN] != nullptr)
|
||||
const UniformInfo *u = builtinUniformInfo[BUILTIN_TEXTURE_MAIN];
|
||||
if (u != nullptr)
|
||||
{
|
||||
texture->retain();
|
||||
if (builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0])
|
||||
builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0]->release();
|
||||
builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0] = texture;
|
||||
if (activeTextures[u->resourceIndex])
|
||||
activeTextures[u->resourceIndex]->release();
|
||||
activeTextures[u->resourceIndex] = texture;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -76,7 +76,6 @@ public:
|
||||
|
||||
int getVertexAttributeIndex(const std::string &name) override;
|
||||
|
||||
const UniformInfo *getUniformInfo(const std::string &name) const override;
|
||||
const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
|
||||
|
||||
void updateUniform(const UniformInfo *info, int count) override;
|
||||
@@ -84,40 +83,32 @@ public:
|
||||
void sendTextures(const UniformInfo *info, graphics::Texture **textures, int count) override;
|
||||
void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
|
||||
|
||||
bool hasUniform(const std::string &name) const override;
|
||||
|
||||
void setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture) override;
|
||||
|
||||
void setMainTex(graphics::Texture *texture);
|
||||
|
||||
private:
|
||||
void calculateUniformBufferSizeAligned();
|
||||
void compileShaders();
|
||||
void createDescriptorSetLayout();
|
||||
void createPipelineLayout();
|
||||
void createDescriptorPoolSizes();
|
||||
void createStreamBuffers();
|
||||
void buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename);
|
||||
void createDescriptorPool();
|
||||
VkDescriptorSet allocateDescriptorSet();
|
||||
|
||||
VkDeviceSize uniformBufferSizeAligned;
|
||||
|
||||
VkPipeline computePipeline;
|
||||
|
||||
uint32_t numTextures;
|
||||
uint32_t numBuffers;
|
||||
uint32_t numBufferViews;
|
||||
|
||||
VkDescriptorSetLayout descriptorSetLayout;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
std::vector<VkDescriptorPoolSize> descriptorPoolSizes;
|
||||
|
||||
// we don't know how much memory we need per frame for the uniform buffer descriptors
|
||||
// we keep a vector of stream buffers that gets dynamically increased if more memory is needed
|
||||
std::vector<StreamBuffer*> streamBuffers;
|
||||
std::vector<std::vector<VkDescriptorPool>> descriptorPools;
|
||||
|
||||
std::vector<VkDescriptorBufferInfo> descriptorBuffers;
|
||||
std::vector<VkDescriptorImageInfo> descriptorImages;
|
||||
std::vector<VkBufferView> descriptorBufferViews;
|
||||
std::vector<VkWriteDescriptorSet> descriptorWrites;
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
|
||||
std::vector<VkShaderModule> shaderModules;
|
||||
|
||||
@@ -126,7 +117,6 @@ private:
|
||||
|
||||
bool isCompute = false;
|
||||
|
||||
std::unordered_map<std::string, graphics::Shader::UniformInfo> uniformInfos;
|
||||
UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
|
||||
|
||||
std::unique_ptr<StreamBuffer> uniformBufferObjectBuffer;
|
||||
@@ -138,7 +128,6 @@ private:
|
||||
std::unordered_map<std::string, int> attributes;
|
||||
|
||||
uint32_t currentFrame;
|
||||
uint32_t currentUsedUniformStreamBuffersCount;
|
||||
uint32_t currentDescriptorPool;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -96,6 +96,11 @@ ptrdiff_t StreamBuffer::getHandle() const
|
||||
return (ptrdiff_t) buffer;
|
||||
}
|
||||
|
||||
size_t StreamBuffer::getGPUReadOffset() const
|
||||
{
|
||||
return (frameIndex * bufferSize) + frameGPUReadOffset;
|
||||
}
|
||||
|
||||
love::graphics::StreamBuffer::MapInfo StreamBuffer::map(size_t /*minsize*/)
|
||||
{
|
||||
// TODO: do we also need to wait until a fence is complete, here?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
|
||||
virtual void unloadVolatile() override;
|
||||
|
||||
size_t getGPUReadOffset() const override;
|
||||
MapInfo map(size_t minsize) override;
|
||||
size_t unmap(size_t usedSize) override;
|
||||
void markUsed(size_t usedSize) override;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -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,115 @@ 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;
|
||||
std::vector<VkFormat> vkviewformats;
|
||||
|
||||
for (int slice = 0; slice < sliceCount; slice++)
|
||||
for (PixelFormat viewformat : viewFormats)
|
||||
{
|
||||
if (viewformat != format)
|
||||
{
|
||||
auto id = slices.get(slice, mip);
|
||||
if (id != nullptr)
|
||||
uploadImageData(id, mip, slice, 0, 0);
|
||||
createFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
TextureFormat f = Vulkan::getTextureFormat(viewformat);
|
||||
vkviewformats.push_back(f.internalFormat);
|
||||
}
|
||||
}
|
||||
|
||||
if (texType == TEXTURE_CUBE || (texType == TEXTURE_2D_ARRAY && 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;
|
||||
|
||||
VkImageFormatListCreateInfo viewFormatsInfo{};
|
||||
viewFormatsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO;
|
||||
|
||||
if (!vkviewformats.empty() && vgfx->getDeviceApiVersion() >= VK_API_VERSION_1_2)
|
||||
{
|
||||
viewFormatsInfo.viewFormatCount = (uint32)vkviewformats.size();
|
||||
viewFormatsInfo.pViewFormats = vkviewformats.data();
|
||||
|
||||
imageInfo.pNext = &viewFormatsInfo;
|
||||
}
|
||||
|
||||
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, format,
|
||||
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++)
|
||||
{
|
||||
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 +216,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 +233,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 +254,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 +285,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 +335,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 +347,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;
|
||||
@@ -324,7 +379,7 @@ void Texture::clear()
|
||||
|
||||
if (imageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
|
||||
|
||||
@@ -332,7 +387,7 @@ void Texture::clear()
|
||||
|
||||
vkCmdClearColorImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1, &range);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
|
||||
}
|
||||
@@ -344,7 +399,7 @@ void Texture::clear()
|
||||
}
|
||||
else
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
|
||||
imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
|
||||
|
||||
@@ -354,7 +409,7 @@ void Texture::clear()
|
||||
|
||||
vkCmdClearDepthStencilImage(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &depthStencilColor, 1, &range);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout,
|
||||
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
|
||||
}
|
||||
@@ -394,7 +449,7 @@ void Texture::generateMipmapsInternal()
|
||||
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
0, static_cast<uint32_t>(getMipmapCount()), 0, static_cast<uint32_t>(layerCount));
|
||||
|
||||
@@ -404,16 +459,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 +484,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 +513,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;
|
||||
@@ -472,7 +527,7 @@ void Texture::generateMipmapsInternal()
|
||||
1, &barrier);
|
||||
}
|
||||
|
||||
void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r)
|
||||
void Texture::uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r)
|
||||
{
|
||||
VkBuffer stagingBuffer;
|
||||
VmaAllocation vmaAllocation;
|
||||
@@ -496,11 +551,11 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
|
||||
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;
|
||||
@@ -520,7 +575,7 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
|
||||
imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
level, 1, baseLayer, 1);
|
||||
|
||||
@@ -533,7 +588,7 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s
|
||||
®ion
|
||||
);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout,
|
||||
level, 1, baseLayer, 1);
|
||||
}
|
||||
@@ -558,8 +613,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{};
|
||||
@@ -572,11 +627,11 @@ void Texture::copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
else
|
||||
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion);
|
||||
@@ -588,8 +643,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{};
|
||||
@@ -603,11 +658,11 @@ void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, (VkBuffer) dest->getHandle(), 1, ®ion);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout);
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout);
|
||||
}
|
||||
else
|
||||
vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, ®ion);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -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;
|
||||
|
||||
@@ -59,9 +61,9 @@ public:
|
||||
VkImageView getRenderTargetView(int mip, int layer);
|
||||
VkSampleCountFlagBits getMsaaSamples() const;
|
||||
|
||||
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
|
||||
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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -291,6 +291,9 @@ TextureFormat Vulkan::getTextureFormat(PixelFormat format)
|
||||
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
|
||||
break;
|
||||
case PIXELFORMAT_DEPTH24_UNORM:
|
||||
textureFormat.internalFormat = VK_FORMAT_X8_D24_UNORM_PACK32;
|
||||
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
|
||||
break;
|
||||
case PIXELFORMAT_DEPTH24_UNORM_STENCIL8:
|
||||
textureFormat.internalFormat = VK_FORMAT_D24_UNORM_S8_UINT;
|
||||
textureFormat.internalFormatRepresentation = FORMATREPRESENTATION_UINT;
|
||||
@@ -816,7 +819,7 @@ VkIndexType Vulkan::getVulkanIndexBufferType(IndexDataType type)
|
||||
}
|
||||
}
|
||||
|
||||
static void setImageLayoutTransitionOptions(bool previous, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags, bool &depthStencil)
|
||||
static void setImageLayoutTransitionOptions(bool previous, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags)
|
||||
{
|
||||
switch (layout)
|
||||
{
|
||||
@@ -838,7 +841,6 @@ static void setImageLayoutTransitionOptions(bool previous, VkImageLayout layout,
|
||||
stageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||
depthStencil = true;
|
||||
accessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
|
||||
stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
break;
|
||||
@@ -863,7 +865,7 @@ static void setImageLayoutTransitionOptions(bool previous, VkImageLayout layout,
|
||||
}
|
||||
}
|
||||
|
||||
void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t baseLevel, uint32_t levelCount, uint32_t baseLayer, uint32_t layerCount)
|
||||
void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, PixelFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t baseLevel, uint32_t levelCount, uint32_t baseLayer, uint32_t layerCount)
|
||||
{
|
||||
VkImageMemoryBarrier barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
@@ -877,17 +879,19 @@ void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage ima
|
||||
barrier.subresourceRange.baseArrayLayer = baseLayer;
|
||||
barrier.subresourceRange.layerCount = layerCount;
|
||||
|
||||
bool depthStencil = false;
|
||||
VkPipelineStageFlags sourceStage;
|
||||
VkPipelineStageFlags destinationStage;
|
||||
|
||||
setImageLayoutTransitionOptions(true, oldLayout, barrier.srcAccessMask, sourceStage, depthStencil);
|
||||
setImageLayoutTransitionOptions(false, newLayout, barrier.dstAccessMask, destinationStage, depthStencil);
|
||||
setImageLayoutTransitionOptions(true, oldLayout, barrier.srcAccessMask, sourceStage);
|
||||
setImageLayoutTransitionOptions(false, newLayout, barrier.dstAccessMask, destinationStage);
|
||||
|
||||
if (depthStencil)
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
else
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
const PixelFormatInfo &info = getPixelFormatInfo(format);
|
||||
if (info.color)
|
||||
barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
if (info.depth)
|
||||
barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
if (info.stencil)
|
||||
barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
|
||||
vkCmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
static VkIndexType getVulkanIndexBufferType(IndexDataType type);
|
||||
|
||||
static void cmdTransitionImageLayout(
|
||||
VkCommandBuffer, VkImage, VkImageLayout oldLayout, VkImageLayout newLayout,
|
||||
VkCommandBuffer, VkImage, PixelFormat format, VkImageLayout oldLayout, VkImageLayout newLayout,
|
||||
uint32_t baseLevel = 0, uint32_t levelCount = VK_REMAINING_MIP_LEVELS, uint32_t baseLayer = 0, uint32_t layerCount = VK_REMAINING_ARRAY_LAYERS);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -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))
|
||||
@@ -824,6 +831,25 @@ static void luax_checktexturesettings(lua_State *L, int idx, bool opt, bool chec
|
||||
|
||||
s.computeWrite = luax_boolflag(L, idx, Texture::getConstant(Texture::SETTING_COMPUTE_WRITE), s.computeWrite);
|
||||
|
||||
lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_VIEW_FORMATS));
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
{
|
||||
if (lua_type(L, -1) != LUA_TTABLE)
|
||||
luaL_argerror(L, idx, "expected field 'viewformats' to be a table type");
|
||||
|
||||
for (int i = 1; i <= luax_objlen(L, -1); i++)
|
||||
{
|
||||
lua_rawgeti(L, -1, i);
|
||||
const char *str = luaL_checkstring(L, -1);
|
||||
PixelFormat viewformat = PIXELFORMAT_UNKNOWN;
|
||||
if (!getConstant(str, viewformat))
|
||||
luax_enumerror(L, "pixel format", str);
|
||||
s.viewFormats.push_back(viewformat);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, idx, Texture::getConstant(Texture::SETTING_READABLE));
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
s.readable.set(luax_checkboolean(L, -1));
|
||||
@@ -1240,6 +1266,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);
|
||||
@@ -3052,13 +3138,12 @@ int w_draw(lua_State *L)
|
||||
{
|
||||
Drawable *drawable = nullptr;
|
||||
Texture *texture = nullptr;
|
||||
Quad *quad = nullptr;
|
||||
Quad *quad = luax_totype<Quad>(L, 2);
|
||||
int startidx = 2;
|
||||
|
||||
if (luax_istype(L, 2, Quad::type))
|
||||
if (quad != nullptr)
|
||||
{
|
||||
texture = luax_checktexture(L, 1);
|
||||
quad = luax_totype<Quad>(L, 2);
|
||||
startidx = 3;
|
||||
}
|
||||
else if (lua_isnil(L, 2) && !lua_isnoneornil(L, 3))
|
||||
@@ -3088,14 +3173,14 @@ int w_draw(lua_State *L)
|
||||
int w_drawLayer(lua_State *L)
|
||||
{
|
||||
Texture *texture = luax_checktexture(L, 1);
|
||||
Quad *quad = nullptr;
|
||||
int layer = (int) luaL_checkinteger(L, 2) - 1;
|
||||
int startidx = 3;
|
||||
|
||||
if (luax_istype(L, startidx, Quad::type))
|
||||
int startidx = 3;
|
||||
Quad *quad = luax_totype<Quad>(L, startidx);
|
||||
|
||||
if (quad != nullptr)
|
||||
{
|
||||
texture = luax_checktexture(L, 1);
|
||||
quad = luax_totype<Quad>(L, startidx);
|
||||
startidx++;
|
||||
}
|
||||
else if (lua_isnil(L, startidx) && !lua_isnoneornil(L, startidx + 1))
|
||||
@@ -3815,25 +3900,31 @@ int w_inverseTransformPoint(lua_State *L)
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_setOrthoProjection(lua_State *L)
|
||||
int w_setCustomProjection(lua_State *L)
|
||||
{
|
||||
float w = (float) luaL_checknumber(L, 1);
|
||||
float h = (float) luaL_checknumber(L, 2);
|
||||
float near = (float) luaL_optnumber(L, 3, -10.0);
|
||||
float far = (float) luaL_optnumber(L, 4, 10.0);
|
||||
math::Transform *transform = luax_totype<math::Transform>(L, 1);
|
||||
if (transform != nullptr)
|
||||
{
|
||||
instance()->setCustomProjection(transform->getMatrix());
|
||||
return 0;
|
||||
}
|
||||
|
||||
luax_catchexcept(L, [&]() { instance()->setOrthoProjection(w, h, near, far); });
|
||||
return 0;
|
||||
}
|
||||
math::Transform::MatrixLayout layout = math::Transform::MATRIX_ROW_MAJOR;
|
||||
|
||||
int w_setPerspectiveProjection(lua_State *L)
|
||||
{
|
||||
float verticalfov = (float) luaL_checknumber(L, 1);
|
||||
float aspect = (float) luaL_checknumber(L, 2);
|
||||
float near = (float) luaL_checknumber(L, 3);
|
||||
float far = (float) luaL_checknumber(L, 4);
|
||||
int idx = 1;
|
||||
if (lua_type(L, idx) == LUA_TSTRING)
|
||||
{
|
||||
const char* layoutstr = lua_tostring(L, idx);
|
||||
if (!math::Transform::getConstant(layoutstr, layout))
|
||||
return luax_enumerror(L, "matrix layout", math::Transform::getConstants(layout), layoutstr);
|
||||
|
||||
luax_catchexcept(L, [&]() { instance()->setPerspectiveProjection(verticalfov, aspect, near, far); });
|
||||
idx++;
|
||||
}
|
||||
|
||||
float elements[16];
|
||||
love::math::luax_checkmatrix(L, idx, layout, elements);
|
||||
|
||||
instance()->setCustomProjection(Matrix4(elements));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3857,6 +3948,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 },
|
||||
@@ -3988,8 +4080,7 @@ static const luaL_Reg functions[] =
|
||||
{ "transformPoint", w_transformPoint },
|
||||
{ "inverseTransformPoint", w_inverseTransformPoint },
|
||||
|
||||
{ "setOrthoProjection", w_setOrthoProjection },
|
||||
{ "setPerspectiveProjection", w_setPerspectiveProjection },
|
||||
{ "setCustomProjection", w_setCustomProjection },
|
||||
{ "resetProjection", w_resetProjection },
|
||||
|
||||
// Deprecated
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -3,7 +3,7 @@ R"luastring"--(
|
||||
-- There is a matching delimiter at the bottom of the file.
|
||||
|
||||
--[[
|
||||
Copyright (c) 2006-2023 LOVE Development Team
|
||||
Copyright (c) 2006-2024 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2023 LOVE Development Team
|
||||
* Copyright (c) 2006-2024 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user