Merge pull request #42119 from lawnjelly/ewok3
Unified GLES3 / GLES2 Batching
This commit is contained in:
commit
123942f61f
28 changed files with 7361 additions and 4495 deletions
|
@ -1062,6 +1062,10 @@
|
|||
<member name="rendering/limits/time/time_rollover_secs" type="float" setter="" getter="" default="3600">
|
||||
Shaders have a time variable that constantly increases. At some point, it needs to be rolled back to zero to avoid precision errors on shader animations. This setting specifies when (in seconds).
|
||||
</member>
|
||||
<member name="rendering/quality/2d/ninepatch_mode" type="int" setter="" getter="" default="0">
|
||||
Choose between default mode where corner scalings are preserved matching the artwork, and scaling mode.
|
||||
Not available in GLES3 when [member rendering/batching/options/use_batching] is off.
|
||||
</member>
|
||||
<member name="rendering/quality/2d/use_nvidia_rect_flicker_workaround" type="bool" setter="" getter="" default="false">
|
||||
Some NVIDIA GPU drivers have a bug which produces flickering issues for the [code]draw_rect[/code] method, especially as used in [TileMap]. Refer to [url=https://github.com/godotengine/godot/issues/9913]GitHub issue 9913[/url] for details.
|
||||
If [code]true[/code], this option enables a "safe" code path for such NVIDIA GPUs at the cost of performance. This option affects GLES2 and GLES3 rendering, but only on desktop platforms.
|
||||
|
@ -1070,6 +1074,10 @@
|
|||
If [code]true[/code], forces snapping of polygons to pixels in 2D rendering. May help in some pixel art styles.
|
||||
Consider using the project setting [member rendering/batching/precision/uv_contract] to prevent artifacts.
|
||||
</member>
|
||||
<member name="rendering/quality/2d/use_software_skinning" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], performs 2d skinning on the CPU rather than the GPU. This provides greater compatibility with a wide range of hardware, and also may be faster in some circumstances.
|
||||
Currently only available when [member rendering/batching/options/use_batching] is active.
|
||||
</member>
|
||||
<member name="rendering/quality/depth/hdr" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], allocates the main framebuffer with high dynamic range. High dynamic range allows the use of [Color] values greater than 1.
|
||||
[b]Note:[/b] Only available on the GLES3 backend.
|
||||
|
|
|
@ -26,6 +26,7 @@ SConscript("winmidi/SCsub")
|
|||
if env["platform"] != "server":
|
||||
SConscript("gles3/SCsub")
|
||||
SConscript("gles2/SCsub")
|
||||
SConscript("gles_common/SCsub")
|
||||
SConscript("gl_context/SCsub")
|
||||
else:
|
||||
SConscript("dummy/SCsub")
|
||||
|
|
|
@ -56,7 +56,12 @@ void RasterizerCanvasBaseGLES2::canvas_begin() {
|
|||
|
||||
// always start with light_angle unset
|
||||
state.using_light_angle = false;
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_LIGHT_ANGLE, false);
|
||||
state.using_large_vertex = false;
|
||||
state.using_modulate = false;
|
||||
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LIGHT_ANGLE, false);
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_MODULATE, false);
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LARGE_VERTEX, false);
|
||||
state.canvas_shader.bind();
|
||||
|
||||
int viewport_x, viewport_y, viewport_width, viewport_height;
|
||||
|
@ -160,13 +165,23 @@ void RasterizerCanvasBaseGLES2::draw_generic_textured_rect(const Rect2 &p_rect,
|
|||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
}
|
||||
|
||||
void RasterizerCanvasBaseGLES2::_set_texture_rect_mode(bool p_texture_rect, bool p_light_angle) {
|
||||
void RasterizerCanvasBaseGLES2::_set_texture_rect_mode(bool p_texture_rect, bool p_light_angle, bool p_modulate, bool p_large_vertex) {
|
||||
// always set this directly (this could be state checked)
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, p_texture_rect);
|
||||
|
||||
if (state.using_light_angle != p_light_angle) {
|
||||
state.using_light_angle = p_light_angle;
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_LIGHT_ANGLE, p_light_angle);
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LIGHT_ANGLE, p_light_angle);
|
||||
}
|
||||
|
||||
if (state.using_modulate != p_modulate) {
|
||||
state.using_modulate = p_modulate;
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_MODULATE, p_modulate);
|
||||
}
|
||||
|
||||
if (state.using_large_vertex != p_large_vertex) {
|
||||
state.using_large_vertex = p_large_vertex;
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LARGE_VERTEX, p_large_vertex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,8 +446,21 @@ void RasterizerCanvasBaseGLES2::_draw_polygon(const int *p_indices, int p_index_
|
|||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
|
||||
|
||||
// precalculate the buffer size ahead of time instead of making multiple glBufferSubData calls
|
||||
uint32_t total_buffer_size = sizeof(Vector2) * p_vertex_count;
|
||||
if (!p_singlecolor && p_colors) {
|
||||
total_buffer_size += sizeof(Color) * p_vertex_count;
|
||||
}
|
||||
if (p_uvs) {
|
||||
total_buffer_size += sizeof(Vector2) * p_vertex_count;
|
||||
}
|
||||
if (p_weights && p_bones) {
|
||||
total_buffer_size += sizeof(float) * 4 * p_vertex_count; // weights
|
||||
total_buffer_size += sizeof(int) * 4 * p_vertex_count; // bones
|
||||
}
|
||||
|
||||
uint32_t buffer_ofs = 0;
|
||||
storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices);
|
||||
storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, total_buffer_size, p_vertices);
|
||||
|
||||
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
|
||||
glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL);
|
||||
|
@ -446,14 +474,12 @@ void RasterizerCanvasBaseGLES2::_draw_polygon(const int *p_indices, int p_index_
|
|||
glDisableVertexAttribArray(VS::ARRAY_COLOR);
|
||||
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
|
||||
} else {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors);
|
||||
glEnableVertexAttribArray(VS::ARRAY_COLOR);
|
||||
glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs));
|
||||
buffer_ofs += sizeof(Color) * p_vertex_count;
|
||||
}
|
||||
|
||||
if (p_uvs) {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs);
|
||||
glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
|
||||
glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs));
|
||||
buffer_ofs += sizeof(Vector2) * p_vertex_count;
|
||||
|
@ -462,12 +488,10 @@ void RasterizerCanvasBaseGLES2::_draw_polygon(const int *p_indices, int p_index_
|
|||
}
|
||||
|
||||
if (p_weights && p_bones) {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights);
|
||||
glEnableVertexAttribArray(VS::ARRAY_WEIGHTS);
|
||||
glVertexAttribPointer(VS::ARRAY_WEIGHTS, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs));
|
||||
buffer_ofs += sizeof(float) * 4 * p_vertex_count;
|
||||
|
||||
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones);
|
||||
glEnableVertexAttribArray(VS::ARRAY_BONES);
|
||||
glVertexAttribPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, GL_FALSE, sizeof(int) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs));
|
||||
buffer_ofs += sizeof(int) * 4 * p_vertex_count;
|
||||
|
@ -501,8 +525,17 @@ void RasterizerCanvasBaseGLES2::_draw_generic(GLuint p_primitive, int p_vertex_c
|
|||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
|
||||
|
||||
// precalculate the buffer size ahead of time instead of making multiple glBufferSubData calls
|
||||
uint32_t total_buffer_size = sizeof(Vector2) * p_vertex_count;
|
||||
if (!p_singlecolor && p_colors) {
|
||||
total_buffer_size += sizeof(Color) * p_vertex_count;
|
||||
}
|
||||
if (p_uvs) {
|
||||
total_buffer_size += sizeof(Vector2) * p_vertex_count;
|
||||
}
|
||||
|
||||
uint32_t buffer_ofs = 0;
|
||||
storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices);
|
||||
storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, total_buffer_size, p_vertices);
|
||||
|
||||
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
|
||||
glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL);
|
||||
|
@ -516,14 +549,12 @@ void RasterizerCanvasBaseGLES2::_draw_generic(GLuint p_primitive, int p_vertex_c
|
|||
glDisableVertexAttribArray(VS::ARRAY_COLOR);
|
||||
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
|
||||
} else {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors);
|
||||
glEnableVertexAttribArray(VS::ARRAY_COLOR);
|
||||
glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs));
|
||||
buffer_ofs += sizeof(Color) * p_vertex_count;
|
||||
}
|
||||
|
||||
if (p_uvs) {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs);
|
||||
glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
|
||||
glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs));
|
||||
} else {
|
||||
|
@ -1024,10 +1055,4 @@ void RasterizerCanvasBaseGLES2::finalize() {
|
|||
}
|
||||
|
||||
RasterizerCanvasBaseGLES2::RasterizerCanvasBaseGLES2() {
|
||||
#ifdef GLES_OVER_GL
|
||||
use_nvidia_rect_workaround = GLOBAL_GET("rendering/quality/2d/use_nvidia_rect_flicker_workaround");
|
||||
#else
|
||||
// Not needed (a priori) on GLES devices
|
||||
use_nvidia_rect_workaround = false;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -31,13 +31,14 @@
|
|||
#ifndef RASTERIZERCANVASBASEGLES2_H
|
||||
#define RASTERIZERCANVASBASEGLES2_H
|
||||
|
||||
#include "rasterizer_array_gles2.h"
|
||||
#include "drivers/gles_common/rasterizer_array.h"
|
||||
#include "rasterizer_storage_gles2.h"
|
||||
#include "servers/visual/rasterizer.h"
|
||||
|
||||
#include "shaders/canvas.glsl.gen.h"
|
||||
#include "shaders/lens_distorted.glsl.gen.h"
|
||||
|
||||
#include "drivers/gles_common/rasterizer_storage_common.h"
|
||||
#include "shaders/canvas_shadow.glsl.gen.h"
|
||||
|
||||
class RasterizerCanvasBaseGLES2 : public RasterizerCanvas {
|
||||
|
@ -77,7 +78,11 @@ public:
|
|||
LensDistortedShaderGLES2 lens_shader;
|
||||
|
||||
bool using_texture_rect;
|
||||
|
||||
bool using_light_angle;
|
||||
bool using_modulate;
|
||||
bool using_large_vertex;
|
||||
|
||||
bool using_ninepatch;
|
||||
bool using_skeleton;
|
||||
|
||||
|
@ -102,8 +107,6 @@ public:
|
|||
|
||||
RasterizerStorageGLES2 *storage;
|
||||
|
||||
bool use_nvidia_rect_workaround;
|
||||
|
||||
void _set_uniforms();
|
||||
|
||||
virtual RID light_internal_create();
|
||||
|
@ -131,7 +134,7 @@ public:
|
|||
virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow);
|
||||
|
||||
RasterizerStorageGLES2::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map);
|
||||
void _set_texture_rect_mode(bool p_texture_rect, bool p_light_angle = false);
|
||||
void _set_texture_rect_mode(bool p_texture_rect, bool p_light_angle = false, bool p_modulate = false, bool p_large_vertex = false);
|
||||
|
||||
void initialize();
|
||||
void finalize();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -31,297 +31,14 @@
|
|||
#ifndef RASTERIZERCANVASGLES2_H
|
||||
#define RASTERIZERCANVASGLES2_H
|
||||
|
||||
#include "drivers/gles_common/rasterizer_canvas_batcher.h"
|
||||
#include "rasterizer_canvas_base_gles2.h"
|
||||
|
||||
class RasterizerSceneGLES2;
|
||||
|
||||
class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 {
|
||||
class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2, public RasterizerCanvasBatcher<RasterizerCanvasGLES2, RasterizerStorageGLES2> {
|
||||
|
||||
// used to determine whether we use hardware transform (none)
|
||||
// software transform all verts, or software transform just a translate
|
||||
// (no rotate or scale)
|
||||
enum TransformMode {
|
||||
TM_NONE,
|
||||
TM_ALL,
|
||||
TM_TRANSLATE,
|
||||
};
|
||||
|
||||
// pod versions of vector and color and RID, need to be 32 bit for vertex format
|
||||
struct BatchVector2 {
|
||||
float x, y;
|
||||
void set(const Vector2 &p_o) {
|
||||
x = p_o.x;
|
||||
y = p_o.y;
|
||||
}
|
||||
void to(Vector2 &r_o) const {
|
||||
r_o.x = x;
|
||||
r_o.y = y;
|
||||
}
|
||||
};
|
||||
|
||||
struct BatchColor {
|
||||
float r, g, b, a;
|
||||
void set(const Color &p_c) {
|
||||
r = p_c.r;
|
||||
g = p_c.g;
|
||||
b = p_c.b;
|
||||
a = p_c.a;
|
||||
}
|
||||
bool operator==(const BatchColor &p_c) const {
|
||||
return (r == p_c.r) && (g == p_c.g) && (b == p_c.b) && (a == p_c.a);
|
||||
}
|
||||
bool operator!=(const BatchColor &p_c) const { return (*this == p_c) == false; }
|
||||
bool equals(const Color &p_c) const {
|
||||
return (r == p_c.r) && (g == p_c.g) && (b == p_c.b) && (a == p_c.a);
|
||||
}
|
||||
const float *get_data() const { return &r; }
|
||||
String to_string() const;
|
||||
};
|
||||
|
||||
struct BatchVertex {
|
||||
// must be 32 bit pod
|
||||
BatchVector2 pos;
|
||||
BatchVector2 uv;
|
||||
};
|
||||
|
||||
struct BatchVertexColored : public BatchVertex {
|
||||
// must be 32 bit pod
|
||||
BatchColor col;
|
||||
};
|
||||
|
||||
struct BatchVertexLightAngled : public BatchVertexColored {
|
||||
// must be pod
|
||||
float light_angle;
|
||||
};
|
||||
|
||||
struct Batch {
|
||||
enum CommandType : uint32_t {
|
||||
BT_DEFAULT,
|
||||
BT_RECT,
|
||||
};
|
||||
|
||||
CommandType type;
|
||||
uint32_t first_command; // also item reference number
|
||||
uint32_t num_commands;
|
||||
uint32_t first_quad;
|
||||
uint32_t batch_texture_id;
|
||||
BatchColor color;
|
||||
};
|
||||
|
||||
struct BatchTex {
|
||||
enum TileMode : uint32_t {
|
||||
TILE_OFF,
|
||||
TILE_NORMAL,
|
||||
TILE_FORCE_REPEAT,
|
||||
};
|
||||
RID RID_texture;
|
||||
RID RID_normal;
|
||||
TileMode tile_mode;
|
||||
BatchVector2 tex_pixel_size;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
// items in a list to be sorted prior to joining
|
||||
struct BSortItem {
|
||||
// have a function to keep as pod, rather than operator
|
||||
void assign(const BSortItem &o) {
|
||||
item = o.item;
|
||||
z_index = o.z_index;
|
||||
}
|
||||
Item *item;
|
||||
int z_index;
|
||||
};
|
||||
|
||||
// batch item may represent 1 or more items
|
||||
struct BItemJoined {
|
||||
uint32_t first_item_ref;
|
||||
uint32_t num_item_refs;
|
||||
|
||||
Rect2 bounding_rect;
|
||||
|
||||
// note the z_index may only be correct for the first of the joined item references
|
||||
// this has implications for light culling with z ranged lights.
|
||||
int16_t z_index;
|
||||
|
||||
// these are defined in RasterizerStorageGLES2::Shader::CanvasItem::BatchFlags
|
||||
uint16_t flags;
|
||||
|
||||
// we are always splitting items with lots of commands,
|
||||
// and items with unhandled primitives (default)
|
||||
bool use_hardware_transform() const { return num_item_refs == 1; }
|
||||
};
|
||||
|
||||
struct BItemRef {
|
||||
Item *item;
|
||||
Color final_modulate;
|
||||
};
|
||||
|
||||
struct BLightRegion {
|
||||
void reset() {
|
||||
light_bitfield = 0;
|
||||
shadow_bitfield = 0;
|
||||
too_many_lights = false;
|
||||
}
|
||||
uint64_t light_bitfield;
|
||||
uint64_t shadow_bitfield;
|
||||
bool too_many_lights; // we can only do light region optimization if there are 64 or less lights
|
||||
};
|
||||
|
||||
struct BatchData {
|
||||
BatchData();
|
||||
void reset_flush() {
|
||||
batches.reset();
|
||||
batch_textures.reset();
|
||||
|
||||
vertices.reset();
|
||||
light_angles.reset();
|
||||
|
||||
total_quads = 0;
|
||||
total_color_changes = 0;
|
||||
use_light_angles = false;
|
||||
}
|
||||
|
||||
GLuint gl_vertex_buffer;
|
||||
GLuint gl_index_buffer;
|
||||
|
||||
uint32_t max_quads;
|
||||
uint32_t vertex_buffer_size_units;
|
||||
uint32_t vertex_buffer_size_bytes;
|
||||
uint32_t index_buffer_size_units;
|
||||
uint32_t index_buffer_size_bytes;
|
||||
|
||||
// small vertex FVF type - pos and UV.
|
||||
// This will always be written to initially, but can be translated
|
||||
// to larger FVFs if necessary.
|
||||
RasterizerArrayGLES2<BatchVertex> vertices;
|
||||
|
||||
// extra data which can be stored during prefilling, for later translation to larger FVFs
|
||||
RasterizerArrayGLES2<float> light_angles;
|
||||
|
||||
// instead of having a different buffer for each vertex FVF type
|
||||
// we have a special array big enough for the biggest FVF
|
||||
// which can have a changeable unit size, and reuse it.
|
||||
RasterizerUnitArrayGLES2 unit_vertices;
|
||||
|
||||
RasterizerArrayGLES2<Batch> batches;
|
||||
RasterizerArrayGLES2<Batch> batches_temp; // used for translating to colored vertex batches
|
||||
RasterizerArray_non_pod_GLES2<BatchTex> batch_textures; // the only reason this is non-POD is because of RIDs
|
||||
|
||||
// flexible vertex format.
|
||||
// all verts have pos and UV.
|
||||
// some have color, some light angles etc.
|
||||
bool use_colored_vertices;
|
||||
bool use_light_angles;
|
||||
|
||||
RasterizerArrayGLES2<BItemJoined> items_joined;
|
||||
RasterizerArrayGLES2<BItemRef> item_refs;
|
||||
|
||||
// items are sorted prior to joining
|
||||
RasterizerArrayGLES2<BSortItem> sort_items;
|
||||
|
||||
// counts
|
||||
int total_quads;
|
||||
|
||||
// we keep a record of how many color changes caused new batches
|
||||
// if the colors are causing an excessive number of batches, we switch
|
||||
// to alternate batching method and add color to the vertex format.
|
||||
int total_color_changes;
|
||||
|
||||
// if the shader is using MODULATE, we prevent baking color so the final_modulate can
|
||||
// be read in the shader.
|
||||
// if the shader is reading VERTEX, we prevent baking vertex positions with extra matrices etc
|
||||
// to prevent the read position being incorrect.
|
||||
// These flags are defined in RasterizerStorageGLES2::Shader::CanvasItem::BatchFlags
|
||||
uint32_t joined_item_batch_flags;
|
||||
|
||||
// measured in pixels, recalculated each frame
|
||||
float scissor_threshold_area;
|
||||
|
||||
// diagnose this frame, every nTh frame when settings_diagnose_frame is on
|
||||
bool diagnose_frame;
|
||||
String frame_string;
|
||||
uint32_t next_diagnose_tick;
|
||||
uint64_t diagnose_frame_number;
|
||||
|
||||
// whether to join items across z_indices - this can interfere with z ranged lights,
|
||||
// so has to be disabled in some circumstances
|
||||
bool join_across_z_indices;
|
||||
|
||||
// global settings
|
||||
bool settings_use_batching; // the current use_batching (affected by flash)
|
||||
bool settings_use_batching_original_choice; // the choice entered in project settings
|
||||
bool settings_flash_batching; // for regression testing, flash between non-batched and batched renderer
|
||||
bool settings_diagnose_frame; // print out batches to help optimize / regression test
|
||||
int settings_max_join_item_commands;
|
||||
float settings_colored_vertex_format_threshold;
|
||||
int settings_batch_buffer_num_verts;
|
||||
bool settings_scissor_lights;
|
||||
float settings_scissor_threshold; // 0.0 to 1.0
|
||||
int settings_item_reordering_lookahead;
|
||||
bool settings_use_single_rect_fallback;
|
||||
int settings_light_max_join_items;
|
||||
|
||||
// uv contraction
|
||||
bool settings_uv_contract;
|
||||
float settings_uv_contract_amount;
|
||||
|
||||
// only done on diagnose frame
|
||||
void reset_stats() {
|
||||
stats_items_sorted = 0;
|
||||
stats_light_items_joined = 0;
|
||||
}
|
||||
|
||||
// frame stats (just for monitoring and debugging)
|
||||
int stats_items_sorted;
|
||||
int stats_light_items_joined;
|
||||
} bdata;
|
||||
|
||||
struct RenderItemState {
|
||||
RenderItemState() { reset(); }
|
||||
void reset();
|
||||
Item *current_clip;
|
||||
RasterizerStorageGLES2::Shader *shader_cache;
|
||||
bool rebind_shader;
|
||||
bool prev_use_skeleton;
|
||||
int last_blend_mode;
|
||||
RID canvas_last_material;
|
||||
Color final_modulate;
|
||||
|
||||
// used for joining items only
|
||||
BItemJoined *joined_item;
|
||||
bool join_batch_break;
|
||||
BLightRegion light_region;
|
||||
|
||||
// 'item group' is data over a single call to canvas_render_items
|
||||
int item_group_z;
|
||||
Color item_group_modulate;
|
||||
Light *item_group_light;
|
||||
Transform2D item_group_base_transform;
|
||||
} _render_item_state;
|
||||
|
||||
struct FillState {
|
||||
void reset() {
|
||||
// don't reset members that need to be preserved after flushing
|
||||
// half way through a list of commands
|
||||
curr_batch = 0;
|
||||
batch_tex_id = -1;
|
||||
texpixel_size = Vector2(1, 1);
|
||||
contract_uvs = false;
|
||||
}
|
||||
Batch *curr_batch;
|
||||
int batch_tex_id;
|
||||
bool use_hardware_transform;
|
||||
bool contract_uvs;
|
||||
Vector2 texpixel_size;
|
||||
Color final_modulate;
|
||||
TransformMode transform_mode;
|
||||
TransformMode orig_transform_mode;
|
||||
|
||||
// support for extra matrices
|
||||
bool extra_matrix_sent; // whether sent on this item (in which case software transform can't be used untl end of item)
|
||||
int transform_extra_command_number_p1; // plus one to allow fast checking against zero
|
||||
Transform2D transform_combined; // final * extra
|
||||
};
|
||||
friend class RasterizerCanvasBatcher<RasterizerCanvasGLES2, RasterizerStorageGLES2>;
|
||||
|
||||
public:
|
||||
virtual void canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
|
||||
|
@ -332,598 +49,27 @@ public:
|
|||
|
||||
private:
|
||||
// legacy codepath .. to remove after testing
|
||||
void _canvas_render_item(Item *p_ci, RenderItemState &r_ris);
|
||||
void _canvas_item_render_commands(Item *p_item, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
|
||||
void _legacy_canvas_render_item(Item *p_ci, RenderItemState &r_ris);
|
||||
|
||||
// high level batch funcs
|
||||
void canvas_render_items_implementation(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
|
||||
void render_joined_item(const BItemJoined &p_bij, RenderItemState &r_ris);
|
||||
void record_items(Item *p_item_list, int p_z);
|
||||
void join_items(Item *p_item_list, int p_z);
|
||||
void join_sorted_items();
|
||||
bool try_join_item(Item *p_ci, RenderItemState &r_ris, bool &r_batch_break);
|
||||
void render_joined_item_commands(const BItemJoined &p_bij, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material, bool p_lit);
|
||||
void render_batches(Item::Command *const *p_commands, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
|
||||
|
||||
bool prefill_joined_item(FillState &r_fill_state, int &r_command_start, Item *p_item, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
|
||||
|
||||
void flush_render_batches(Item *p_first_item, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
|
||||
|
||||
// low level batch funcs
|
||||
int _batch_find_or_create_tex(const RID &p_texture, const RID &p_normal, bool p_tile, int p_previous_match);
|
||||
RasterizerStorageGLES2::Texture *_get_canvas_texture(const RID &p_texture) const;
|
||||
void _batch_upload_buffers();
|
||||
void _batch_render_rects(const Batch &p_batch, RasterizerStorageGLES2::Material *p_material);
|
||||
BatchVertex *_batch_vertex_request_new() { return bdata.vertices.request(); }
|
||||
Batch *_batch_request_new(bool p_blank = true);
|
||||
void _batch_render_polys(const Batch &p_batch, RasterizerStorageGLES2::Material *p_material);
|
||||
void _batch_render_lines(const Batch &p_batch, RasterizerStorageGLES2::Material *p_material, bool p_anti_alias);
|
||||
|
||||
bool _detect_batch_break(Item *p_ci);
|
||||
void _software_transform_vertex(BatchVector2 &r_v, const Transform2D &p_tr) const;
|
||||
void _software_transform_vertex(Vector2 &r_v, const Transform2D &p_tr) const;
|
||||
TransformMode _find_transform_mode(const Transform2D &p_tr) const;
|
||||
void _prefill_default_batch(FillState &r_fill_state, int p_command_num, const Item &p_item);
|
||||
|
||||
// sorting
|
||||
void sort_items();
|
||||
bool sort_items_from(int p_start);
|
||||
bool _sort_items_match(const BSortItem &p_a, const BSortItem &p_b) const;
|
||||
|
||||
// light scissoring
|
||||
bool _light_find_intersection(const Rect2 &p_item_rect, const Transform2D &p_light_xform, const Rect2 &p_light_rect, Rect2 &r_cliprect) const;
|
||||
bool _light_scissor_begin(const Rect2 &p_item_rect, const Transform2D &p_light_xform, const Rect2 &p_light_rect) const;
|
||||
void _calculate_scissor_threshold_area();
|
||||
|
||||
// no need to compile these in in release, they are unneeded outside the editor and only add to executable size
|
||||
#ifdef DEBUG_ENABLED
|
||||
void diagnose_batches(Item::Command *const *p_commands);
|
||||
String get_command_type_string(const Item::Command &p_command) const;
|
||||
#endif
|
||||
// funcs used from rasterizer_canvas_batcher template
|
||||
void gl_enable_scissor(int p_x, int p_y, int p_width, int p_height) const;
|
||||
void gl_disable_scissor() const;
|
||||
|
||||
public:
|
||||
void initialize();
|
||||
RasterizerCanvasGLES2();
|
||||
|
||||
private:
|
||||
template <bool SEND_LIGHT_ANGLES>
|
||||
bool prefill_rect(Item::CommandRect *rect, FillState &r_fill_state, int &r_command_start, int command_num, int command_count, Item::Command *const *commands, Item *p_item, bool multiply_final_modulate);
|
||||
|
||||
template <class BATCH_VERTEX_TYPE, bool INCLUDE_LIGHT_ANGLES>
|
||||
void _translate_batches_to_larger_FVF();
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
// Default batches will not occur in software transform only items
|
||||
// EXCEPT IN THE CASE OF SINGLE RECTS (and this may well not occur, check the logic in prefill_join_item TYPE_RECT)
|
||||
// but can occur where transform commands have been sent during hardware batch
|
||||
inline void RasterizerCanvasGLES2::_prefill_default_batch(FillState &r_fill_state, int p_command_num, const Item &p_item) {
|
||||
if (r_fill_state.curr_batch->type == Batch::BT_DEFAULT) {
|
||||
// don't need to flush an extra transform command?
|
||||
if (!r_fill_state.transform_extra_command_number_p1) {
|
||||
// another default command, just add to the existing batch
|
||||
r_fill_state.curr_batch->num_commands++;
|
||||
} else {
|
||||
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
|
||||
if (r_fill_state.transform_extra_command_number_p1 != p_command_num) {
|
||||
WARN_PRINT_ONCE("_prefill_default_batch : transform_extra_command_number_p1 != p_command_num");
|
||||
}
|
||||
#endif
|
||||
// if the first member of the batch is a transform we have to be careful
|
||||
if (!r_fill_state.curr_batch->num_commands) {
|
||||
// there can be leading useless extra transforms (sometimes happens with debug collision polys)
|
||||
// we need to rejig the first_command for the first useful transform
|
||||
r_fill_state.curr_batch->first_command += r_fill_state.transform_extra_command_number_p1 - 1;
|
||||
}
|
||||
|
||||
// we do have a pending extra transform command to flush
|
||||
// either the extra transform is in the prior command, or not, in which case we need 2 batches
|
||||
r_fill_state.curr_batch->num_commands += 2;
|
||||
|
||||
r_fill_state.transform_extra_command_number_p1 = 0; // mark as sent
|
||||
r_fill_state.extra_matrix_sent = true;
|
||||
|
||||
// the original mode should always be hardware transform ..
|
||||
// test this assumption
|
||||
//CRASH_COND(r_fill_state.orig_transform_mode != TM_NONE);
|
||||
r_fill_state.transform_mode = r_fill_state.orig_transform_mode;
|
||||
|
||||
// do we need to restore anything else?
|
||||
}
|
||||
} else {
|
||||
// end of previous different type batch, so start new default batch
|
||||
|
||||
// first consider whether there is a dirty extra matrix to send
|
||||
if (r_fill_state.transform_extra_command_number_p1) {
|
||||
// get which command the extra is in, and blank all the records as it no longer is stored CPU side
|
||||
int extra_command = r_fill_state.transform_extra_command_number_p1 - 1; // plus 1 based
|
||||
r_fill_state.transform_extra_command_number_p1 = 0;
|
||||
r_fill_state.extra_matrix_sent = true;
|
||||
|
||||
// send the extra to the GPU in a batch
|
||||
r_fill_state.curr_batch = _batch_request_new();
|
||||
r_fill_state.curr_batch->type = Batch::BT_DEFAULT;
|
||||
r_fill_state.curr_batch->first_command = extra_command;
|
||||
r_fill_state.curr_batch->num_commands = 1;
|
||||
|
||||
// revert to the original transform mode
|
||||
// e.g. go back to NONE if we were in hardware transform mode
|
||||
r_fill_state.transform_mode = r_fill_state.orig_transform_mode;
|
||||
|
||||
// reset the original transform if we are going back to software mode,
|
||||
// because the extra is now done on the GPU...
|
||||
// (any subsequent extras are sent directly to the GPU, no deferring)
|
||||
if (r_fill_state.orig_transform_mode != TM_NONE) {
|
||||
r_fill_state.transform_combined = p_item.final_transform;
|
||||
}
|
||||
|
||||
// can possibly combine batch with the next one in some cases
|
||||
// this is more efficient than having an extra batch especially for the extra
|
||||
if ((extra_command + 1) == p_command_num) {
|
||||
r_fill_state.curr_batch->num_commands = 2;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// start default batch
|
||||
r_fill_state.curr_batch = _batch_request_new();
|
||||
r_fill_state.curr_batch->type = Batch::BT_DEFAULT;
|
||||
r_fill_state.curr_batch->first_command = p_command_num;
|
||||
r_fill_state.curr_batch->num_commands = 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline void RasterizerCanvasGLES2::_software_transform_vertex(BatchVector2 &r_v, const Transform2D &p_tr) const {
|
||||
Vector2 vc(r_v.x, r_v.y);
|
||||
vc = p_tr.xform(vc);
|
||||
r_v.set(vc);
|
||||
}
|
||||
|
||||
inline void RasterizerCanvasGLES2::_software_transform_vertex(Vector2 &r_v, const Transform2D &p_tr) const {
|
||||
r_v = p_tr.xform(r_v);
|
||||
}
|
||||
|
||||
inline RasterizerCanvasGLES2::TransformMode RasterizerCanvasGLES2::_find_transform_mode(const Transform2D &p_tr) const {
|
||||
// decided whether to do translate only for software transform
|
||||
if ((p_tr.elements[0].x == 1.0) &&
|
||||
(p_tr.elements[0].y == 0.0) &&
|
||||
(p_tr.elements[1].x == 0.0) &&
|
||||
(p_tr.elements[1].y == 1.0)) {
|
||||
return TM_TRANSLATE;
|
||||
}
|
||||
|
||||
return TM_ALL;
|
||||
}
|
||||
|
||||
inline bool RasterizerCanvasGLES2::_sort_items_match(const BSortItem &p_a, const BSortItem &p_b) const {
|
||||
const Item *a = p_a.item;
|
||||
const Item *b = p_b.item;
|
||||
|
||||
if (b->commands.size() != 1)
|
||||
return false;
|
||||
|
||||
// tested outside function
|
||||
// if (a->commands.size() != 1)
|
||||
// return false;
|
||||
|
||||
const Item::Command &cb = *b->commands[0];
|
||||
if (cb.type != Item::Command::TYPE_RECT)
|
||||
return false;
|
||||
|
||||
const Item::Command &ca = *a->commands[0];
|
||||
// tested outside function
|
||||
// if (ca.type != Item::Command::TYPE_RECT)
|
||||
// return false;
|
||||
|
||||
const Item::CommandRect *rect_a = static_cast<const Item::CommandRect *>(&ca);
|
||||
const Item::CommandRect *rect_b = static_cast<const Item::CommandRect *>(&cb);
|
||||
|
||||
if (rect_a->texture != rect_b->texture)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// TEMPLATE FUNCS
|
||||
|
||||
// Translation always involved adding color to the FVF, which enables
|
||||
// joining of batches that have different colors.
|
||||
// There is a trade off. Non colored verts are smaller so work faster, but
|
||||
// there comes a point where it is better to just use colored verts to avoid lots of
|
||||
// batches.
|
||||
// In addition this can optionally add light angles to the FVF, necessary for normal mapping.
|
||||
template <class BATCH_VERTEX_TYPE, bool INCLUDE_LIGHT_ANGLES>
|
||||
void RasterizerCanvasGLES2::_translate_batches_to_larger_FVF() {
|
||||
|
||||
// zeros the size and sets up how big each unit is
|
||||
bdata.unit_vertices.prepare(sizeof(BATCH_VERTEX_TYPE));
|
||||
bdata.batches_temp.reset();
|
||||
|
||||
// As the vertices_colored and batches_temp are 'mirrors' of the non-colored version,
|
||||
// the sizes should be equal, and allocations should never fail. Hence the use of debug
|
||||
// asserts to check program flow, these should not occur at runtime unless the allocation
|
||||
// code has been altered.
|
||||
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
|
||||
CRASH_COND(bdata.unit_vertices.max_size() != bdata.vertices.max_size());
|
||||
CRASH_COND(bdata.batches_temp.max_size() != bdata.batches.max_size());
|
||||
#endif
|
||||
|
||||
Color curr_col(-1.0, -1.0, -1.0, -1.0);
|
||||
|
||||
Batch *dest_batch = 0;
|
||||
|
||||
const float *source_light_angles = &bdata.light_angles[0];
|
||||
|
||||
// translate the batches into vertex colored batches
|
||||
for (int n = 0; n < bdata.batches.size(); n++) {
|
||||
const Batch &source_batch = bdata.batches[n];
|
||||
|
||||
// does source batch use light angles?
|
||||
const BatchTex &btex = bdata.batch_textures[source_batch.batch_texture_id];
|
||||
bool source_batch_uses_light_angles = btex.RID_normal != RID();
|
||||
|
||||
bool needs_new_batch = true;
|
||||
|
||||
if (dest_batch) {
|
||||
if (dest_batch->type == source_batch.type) {
|
||||
if (source_batch.type == Batch::BT_RECT) {
|
||||
if (dest_batch->batch_texture_id == source_batch.batch_texture_id) {
|
||||
// add to previous batch
|
||||
dest_batch->num_commands += source_batch.num_commands;
|
||||
needs_new_batch = false;
|
||||
|
||||
// create the colored verts (only if not default)
|
||||
int first_vert = source_batch.first_quad * 4;
|
||||
int end_vert = 4 * (source_batch.first_quad + source_batch.num_commands);
|
||||
|
||||
for (int v = first_vert; v < end_vert; v++) {
|
||||
const BatchVertex &bv = bdata.vertices[v];
|
||||
BATCH_VERTEX_TYPE *cv = (BatchVertexLightAngled *)bdata.unit_vertices.request();
|
||||
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
|
||||
CRASH_COND(!cv);
|
||||
#endif
|
||||
cv->pos = bv.pos;
|
||||
cv->uv = bv.uv;
|
||||
cv->col = source_batch.color;
|
||||
|
||||
if (INCLUDE_LIGHT_ANGLES) {
|
||||
// this is required to allow compilation with non light angle vertex.
|
||||
// it should be compiled out.
|
||||
BatchVertexLightAngled *lv = (BatchVertexLightAngled *)cv;
|
||||
if (source_batch_uses_light_angles)
|
||||
lv->light_angle = *source_light_angles++;
|
||||
else
|
||||
lv->light_angle = 0.0f; // dummy, unused in vertex shader (could possibly be left uninitialized, but probably bad idea)
|
||||
}
|
||||
}
|
||||
} // textures match
|
||||
} else {
|
||||
// default
|
||||
// we can still join, but only under special circumstances
|
||||
// does this ever happen? not sure at this stage, but left for future expansion
|
||||
uint32_t source_last_command = source_batch.first_command + source_batch.num_commands;
|
||||
if (source_last_command == dest_batch->first_command) {
|
||||
dest_batch->num_commands += source_batch.num_commands;
|
||||
needs_new_batch = false;
|
||||
} // if the commands line up exactly
|
||||
}
|
||||
} // if both batches are the same type
|
||||
|
||||
} // if dest batch is valid
|
||||
|
||||
if (needs_new_batch) {
|
||||
dest_batch = bdata.batches_temp.request();
|
||||
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
|
||||
CRASH_COND(!dest_batch);
|
||||
#endif
|
||||
|
||||
*dest_batch = source_batch;
|
||||
|
||||
// create the colored verts (only if not default)
|
||||
if (source_batch.type != Batch::BT_DEFAULT) {
|
||||
int first_vert = source_batch.first_quad * 4;
|
||||
int end_vert = 4 * (source_batch.first_quad + source_batch.num_commands);
|
||||
|
||||
for (int v = first_vert; v < end_vert; v++) {
|
||||
const BatchVertex &bv = bdata.vertices[v];
|
||||
BATCH_VERTEX_TYPE *cv = (BatchVertexLightAngled *)bdata.unit_vertices.request();
|
||||
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
|
||||
CRASH_COND(!cv);
|
||||
#endif
|
||||
cv->pos = bv.pos;
|
||||
cv->uv = bv.uv;
|
||||
cv->col = source_batch.color;
|
||||
|
||||
if (INCLUDE_LIGHT_ANGLES) {
|
||||
// this is required to allow compilation with non light angle vertex.
|
||||
// it should be compiled out.
|
||||
BatchVertexLightAngled *lv = (BatchVertexLightAngled *)cv;
|
||||
if (source_batch_uses_light_angles)
|
||||
lv->light_angle = *source_light_angles++;
|
||||
else
|
||||
lv->light_angle = 0.0f; // dummy, unused in vertex shader (could possibly be left uninitialized, but probably bad idea)
|
||||
} // if using light angles
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy the temporary batches to the master batch list (this could be avoided but it makes the code cleaner)
|
||||
bdata.batches.copy_from(bdata.batches_temp);
|
||||
}
|
||||
|
||||
// return true if buffer full up, else return false
|
||||
template <bool SEND_LIGHT_ANGLES>
|
||||
bool RasterizerCanvasGLES2::prefill_rect(Item::CommandRect *rect, FillState &r_fill_state, int &r_command_start, int command_num, int command_count, Item::Command *const *commands, Item *p_item, bool multiply_final_modulate) {
|
||||
bool change_batch = false;
|
||||
|
||||
// conditions for creating a new batch
|
||||
if (r_fill_state.curr_batch->type != Batch::BT_RECT) {
|
||||
change_batch = true;
|
||||
|
||||
// check for special case if there is only a single or small number of rects,
|
||||
// in which case we will use the legacy default rect renderer
|
||||
// because it is faster for single rects
|
||||
|
||||
// we only want to do this if not a joined item with more than 1 item,
|
||||
// because joined items with more than 1, the command * will be incorrect
|
||||
// NOTE - this is assuming that use_hardware_transform means that it is a non-joined item!!
|
||||
// If that assumption is incorrect this will go horribly wrong.
|
||||
if (bdata.settings_use_single_rect_fallback && r_fill_state.use_hardware_transform) {
|
||||
bool is_single_rect = false;
|
||||
int command_num_next = command_num + 1;
|
||||
if (command_num_next < command_count) {
|
||||
Item::Command *command_next = commands[command_num_next];
|
||||
if ((command_next->type != Item::Command::TYPE_RECT) && (command_next->type != Item::Command::TYPE_TRANSFORM)) {
|
||||
is_single_rect = true;
|
||||
}
|
||||
} else {
|
||||
is_single_rect = true;
|
||||
}
|
||||
// if it is a rect on its own, do exactly the same as the default routine
|
||||
if (is_single_rect) {
|
||||
_prefill_default_batch(r_fill_state, command_num, *p_item);
|
||||
return false;
|
||||
}
|
||||
} // if use hardware transform
|
||||
}
|
||||
|
||||
Color col = rect->modulate;
|
||||
if (multiply_final_modulate) {
|
||||
col *= r_fill_state.final_modulate;
|
||||
}
|
||||
|
||||
// instead of doing all the texture preparation for EVERY rect,
|
||||
// we build a list of texture combinations and do this once off.
|
||||
// This means we have a potentially rather slow step to identify which texture combo
|
||||
// using the RIDs.
|
||||
int old_batch_tex_id = r_fill_state.batch_tex_id;
|
||||
r_fill_state.batch_tex_id = _batch_find_or_create_tex(rect->texture, rect->normal_map, rect->flags & CANVAS_RECT_TILE, old_batch_tex_id);
|
||||
|
||||
//r_fill_state.use_light_angles = send_light_angles;
|
||||
if (SEND_LIGHT_ANGLES)
|
||||
bdata.use_light_angles = true;
|
||||
|
||||
// try to create vertices BEFORE creating a batch,
|
||||
// because if the vertex buffer is full, we need to finish this
|
||||
// function, draw what we have so far, and then start a new set of batches
|
||||
|
||||
// request FOUR vertices at a time, this is more efficient
|
||||
BatchVertex *bvs = bdata.vertices.request(4);
|
||||
if (!bvs) {
|
||||
// run out of space in the vertex buffer .. finish this function and draw what we have so far
|
||||
// return where we got to
|
||||
r_command_start = command_num;
|
||||
return true;
|
||||
}
|
||||
|
||||
// conditions for creating a new batch
|
||||
if (old_batch_tex_id != r_fill_state.batch_tex_id) {
|
||||
change_batch = true;
|
||||
}
|
||||
|
||||
// we need to treat color change separately because we need to count these
|
||||
// to decide whether to switch on the fly to colored vertices.
|
||||
if (!r_fill_state.curr_batch->color.equals(col)) {
|
||||
change_batch = true;
|
||||
bdata.total_color_changes++;
|
||||
}
|
||||
|
||||
if (change_batch) {
|
||||
// put the tex pixel size in a local (less verbose and can be a register)
|
||||
const BatchTex &batchtex = bdata.batch_textures[r_fill_state.batch_tex_id];
|
||||
batchtex.tex_pixel_size.to(r_fill_state.texpixel_size);
|
||||
|
||||
if (bdata.settings_uv_contract) {
|
||||
r_fill_state.contract_uvs = (batchtex.flags & VS::TEXTURE_FLAG_FILTER) == 0;
|
||||
}
|
||||
|
||||
// need to preserve texpixel_size between items
|
||||
r_fill_state.texpixel_size = r_fill_state.texpixel_size;
|
||||
|
||||
// open new batch (this should never fail, it dynamically grows)
|
||||
r_fill_state.curr_batch = _batch_request_new(false);
|
||||
|
||||
r_fill_state.curr_batch->type = Batch::BT_RECT;
|
||||
r_fill_state.curr_batch->color.set(col);
|
||||
r_fill_state.curr_batch->batch_texture_id = r_fill_state.batch_tex_id;
|
||||
r_fill_state.curr_batch->first_command = command_num;
|
||||
r_fill_state.curr_batch->num_commands = 1;
|
||||
r_fill_state.curr_batch->first_quad = bdata.total_quads;
|
||||
} else {
|
||||
// we could alternatively do the count when closing a batch .. perhaps more efficient
|
||||
r_fill_state.curr_batch->num_commands++;
|
||||
}
|
||||
|
||||
// fill the quad geometry
|
||||
Vector2 mins = rect->rect.position;
|
||||
|
||||
if (r_fill_state.transform_mode == TM_TRANSLATE) {
|
||||
_software_transform_vertex(mins, r_fill_state.transform_combined);
|
||||
}
|
||||
|
||||
Vector2 maxs = mins + rect->rect.size;
|
||||
|
||||
// just aliases
|
||||
BatchVertex *bA = &bvs[0];
|
||||
BatchVertex *bB = &bvs[1];
|
||||
BatchVertex *bC = &bvs[2];
|
||||
BatchVertex *bD = &bvs[3];
|
||||
|
||||
bA->pos.x = mins.x;
|
||||
bA->pos.y = mins.y;
|
||||
|
||||
bB->pos.x = maxs.x;
|
||||
bB->pos.y = mins.y;
|
||||
|
||||
bC->pos.x = maxs.x;
|
||||
bC->pos.y = maxs.y;
|
||||
|
||||
bD->pos.x = mins.x;
|
||||
bD->pos.y = maxs.y;
|
||||
|
||||
// possibility of applying flips here for normal mapping .. but they don't seem to be used
|
||||
if (rect->rect.size.x < 0) {
|
||||
SWAP(bA->pos, bB->pos);
|
||||
SWAP(bC->pos, bD->pos);
|
||||
}
|
||||
if (rect->rect.size.y < 0) {
|
||||
SWAP(bA->pos, bD->pos);
|
||||
SWAP(bB->pos, bC->pos);
|
||||
}
|
||||
|
||||
if (r_fill_state.transform_mode == TM_ALL) {
|
||||
_software_transform_vertex(bA->pos, r_fill_state.transform_combined);
|
||||
_software_transform_vertex(bB->pos, r_fill_state.transform_combined);
|
||||
_software_transform_vertex(bC->pos, r_fill_state.transform_combined);
|
||||
_software_transform_vertex(bD->pos, r_fill_state.transform_combined);
|
||||
}
|
||||
|
||||
// uvs
|
||||
Vector2 src_min;
|
||||
Vector2 src_max;
|
||||
if (rect->flags & CANVAS_RECT_REGION) {
|
||||
src_min = rect->source.position;
|
||||
src_max = src_min + rect->source.size;
|
||||
|
||||
src_min *= r_fill_state.texpixel_size;
|
||||
src_max *= r_fill_state.texpixel_size;
|
||||
|
||||
const float uv_epsilon = bdata.settings_uv_contract_amount;
|
||||
|
||||
// nudge offset for the maximum to prevent precision error on GPU reading into line outside the source rect
|
||||
// this is very difficult to get right.
|
||||
if (r_fill_state.contract_uvs) {
|
||||
src_min.x += uv_epsilon;
|
||||
src_min.y += uv_epsilon;
|
||||
src_max.x -= uv_epsilon;
|
||||
src_max.y -= uv_epsilon;
|
||||
}
|
||||
} else {
|
||||
src_min = Vector2(0, 0);
|
||||
src_max = Vector2(1, 1);
|
||||
}
|
||||
|
||||
// 10% faster calculating the max first
|
||||
Vector2 uvs[4] = {
|
||||
src_min,
|
||||
Vector2(src_max.x, src_min.y),
|
||||
src_max,
|
||||
Vector2(src_min.x, src_max.y),
|
||||
};
|
||||
|
||||
// for encoding in light angle
|
||||
// flips should be optimized out when not being used for light angle.
|
||||
bool flip_h = false;
|
||||
bool flip_v = false;
|
||||
|
||||
if (rect->flags & CANVAS_RECT_TRANSPOSE) {
|
||||
SWAP(uvs[1], uvs[3]);
|
||||
}
|
||||
|
||||
if (rect->flags & CANVAS_RECT_FLIP_H) {
|
||||
SWAP(uvs[0], uvs[1]);
|
||||
SWAP(uvs[2], uvs[3]);
|
||||
flip_h = !flip_h;
|
||||
flip_v = !flip_v;
|
||||
}
|
||||
if (rect->flags & CANVAS_RECT_FLIP_V) {
|
||||
SWAP(uvs[0], uvs[3]);
|
||||
SWAP(uvs[1], uvs[2]);
|
||||
flip_v = !flip_v;
|
||||
}
|
||||
|
||||
bA->uv.set(uvs[0]);
|
||||
bB->uv.set(uvs[1]);
|
||||
bC->uv.set(uvs[2]);
|
||||
bD->uv.set(uvs[3]);
|
||||
|
||||
if (SEND_LIGHT_ANGLES) {
|
||||
// we can either keep the light angles in sync with the verts when writing,
|
||||
// or sync them up during translation. We are syncing in translation.
|
||||
// N.B. There may be batches that don't require light_angles between batches that do.
|
||||
float *angles = bdata.light_angles.request(4);
|
||||
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
|
||||
CRASH_COND(angles == nullptr);
|
||||
#endif
|
||||
|
||||
float angle = 0.0f;
|
||||
const float TWO_PI = Math_PI * 2;
|
||||
|
||||
if (r_fill_state.transform_mode != TM_NONE) {
|
||||
|
||||
const Transform2D &tr = r_fill_state.transform_combined;
|
||||
|
||||
// apply to an x axis
|
||||
// the x axis and y axis can be taken directly from the transform (no need to xform identity vectors)
|
||||
Vector2 x_axis(tr.elements[0][0], tr.elements[1][0]);
|
||||
|
||||
// have to do a y axis to check for scaling flips
|
||||
// this is hassle and extra slowness. We could only allow flips via the flags.
|
||||
Vector2 y_axis(tr.elements[0][1], tr.elements[1][1]);
|
||||
|
||||
// has the x / y axis flipped due to scaling?
|
||||
float cross = x_axis.cross(y_axis);
|
||||
if (cross < 0.0f) {
|
||||
flip_v = !flip_v;
|
||||
}
|
||||
|
||||
// passing an angle is smaller than a vector, it can be reconstructed in the shader
|
||||
angle = x_axis.angle();
|
||||
|
||||
// we don't want negative angles, as negative is used to encode flips.
|
||||
// This moves range from -PI to PI to 0 to TWO_PI
|
||||
if (angle < 0.0f)
|
||||
angle += TWO_PI;
|
||||
|
||||
} // if transform needed
|
||||
|
||||
// if horizontal flip, angle is shifted by 180 degrees
|
||||
if (flip_h) {
|
||||
angle += Math_PI;
|
||||
|
||||
// mod to get back to 0 to TWO_PI range
|
||||
angle = fmodf(angle, TWO_PI);
|
||||
}
|
||||
|
||||
// add 1 (to take care of zero floating point error with sign)
|
||||
angle += 1.0f;
|
||||
|
||||
// flip if necessary to indicate a vertical flip in the shader
|
||||
if (flip_v)
|
||||
angle *= -1.0f;
|
||||
|
||||
// light angle must be sent for each vert, instead as a single uniform in the uniform draw method
|
||||
// this has the benefit of enabling batching with light angles.
|
||||
for (int n = 0; n < 4; n++) {
|
||||
angles[n] = angle;
|
||||
}
|
||||
}
|
||||
|
||||
// increment quad count
|
||||
bdata.total_quads++;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // RASTERIZERCANVASGLES2_H
|
||||
|
|
|
@ -483,6 +483,42 @@ void RasterizerGLES2::make_current() {
|
|||
void RasterizerGLES2::register_config() {
|
||||
}
|
||||
|
||||
// returns NULL if no error, or an error string
|
||||
const char *RasterizerGLES2::gl_check_for_error(bool p_print_error) {
|
||||
GLenum err = glGetError();
|
||||
|
||||
const char *err_string = nullptr;
|
||||
|
||||
switch (err) {
|
||||
default: {
|
||||
// not recognised
|
||||
} break;
|
||||
case GL_NO_ERROR: {
|
||||
} break;
|
||||
case GL_INVALID_ENUM: {
|
||||
err_string = "GL_INVALID_ENUM";
|
||||
} break;
|
||||
case GL_INVALID_VALUE: {
|
||||
err_string = "GL_INVALID_VALUE";
|
||||
} break;
|
||||
case GL_INVALID_OPERATION: {
|
||||
err_string = "GL_INVALID_OPERATION";
|
||||
} break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION: {
|
||||
err_string = "GL_INVALID_FRAMEBUFFER_OPERATION";
|
||||
} break;
|
||||
case GL_OUT_OF_MEMORY: {
|
||||
err_string = "GL_OUT_OF_MEMORY";
|
||||
} break;
|
||||
}
|
||||
|
||||
if (p_print_error && err_string) {
|
||||
print_line(err_string);
|
||||
}
|
||||
|
||||
return err_string;
|
||||
}
|
||||
|
||||
RasterizerGLES2::RasterizerGLES2() {
|
||||
|
||||
storage = memnew(RasterizerStorageGLES2);
|
||||
|
|
|
@ -71,6 +71,8 @@ public:
|
|||
|
||||
virtual bool is_low_end() const { return true; }
|
||||
|
||||
virtual const char *gl_check_for_error(bool p_print_error = true);
|
||||
|
||||
RasterizerGLES2();
|
||||
~RasterizerGLES2();
|
||||
};
|
||||
|
|
|
@ -1452,6 +1452,7 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const {
|
|||
shaders.actions_canvas.usage_flag_pointers["TIME"] = &p_shader->canvas_item.uses_time;
|
||||
shaders.actions_canvas.usage_flag_pointers["MODULATE"] = &p_shader->canvas_item.uses_modulate;
|
||||
shaders.actions_canvas.usage_flag_pointers["COLOR"] = &p_shader->canvas_item.uses_color;
|
||||
|
||||
shaders.actions_canvas.usage_flag_pointers["VERTEX"] = &p_shader->canvas_item.uses_vertex;
|
||||
|
||||
actions = &shaders.actions_canvas;
|
||||
|
@ -1552,10 +1553,10 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const {
|
|||
// some logic for batching
|
||||
if (p_shader->mode == VS::SHADER_CANVAS_ITEM) {
|
||||
if (p_shader->canvas_item.uses_modulate | p_shader->canvas_item.uses_color) {
|
||||
p_shader->canvas_item.batch_flags |= Shader::CanvasItem::PREVENT_COLOR_BAKING;
|
||||
p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_COLOR_BAKING;
|
||||
}
|
||||
if (p_shader->canvas_item.uses_vertex) {
|
||||
p_shader->canvas_item.batch_flags |= Shader::CanvasItem::PREVENT_VERTEX_BAKING;
|
||||
p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_VERTEX_BAKING;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3787,14 +3788,17 @@ void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<
|
|||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer);
|
||||
|
||||
uint32_t buffer_size = p_size * sizeof(float);
|
||||
|
||||
if (p_size > resources.skeleton_transform_buffer_size) {
|
||||
// new requested buffer is bigger, so resizing the GPU buffer
|
||||
|
||||
resources.skeleton_transform_buffer_size = p_size;
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, p_size * sizeof(float), p_data.read().ptr(), GL_DYNAMIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, buffer_size, p_data.read().ptr(), GL_DYNAMIC_DRAW);
|
||||
} else {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, p_size * sizeof(float), p_data.read().ptr());
|
||||
// this may not be best, it could be better to use glBufferData in both cases.
|
||||
buffer_orphan_and_upload(resources.skeleton_transform_buffer_size, 0, buffer_size, p_data.read().ptr());
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
|
|
@ -40,11 +40,6 @@
|
|||
|
||||
#include "shaders/copy.glsl.gen.h"
|
||||
#include "shaders/cubemap_filter.glsl.gen.h"
|
||||
/*
|
||||
#include "shaders/blend_shape.glsl.gen.h"
|
||||
#include "shaders/canvas.glsl.gen.h"
|
||||
#include "shaders/particles.glsl.gen.h"
|
||||
*/
|
||||
|
||||
class RasterizerCanvasGLES2;
|
||||
class RasterizerSceneGLES2;
|
||||
|
@ -450,10 +445,7 @@ public:
|
|||
// these flags are specifically for batching
|
||||
// some of the logic is thus in rasterizer_storage.cpp
|
||||
// we could alternatively set bitflags for each 'uses' and test on the fly
|
||||
enum BatchFlags {
|
||||
PREVENT_COLOR_BAKING = 1 << 0,
|
||||
PREVENT_VERTEX_BAKING = 1 << 1,
|
||||
};
|
||||
// defined in RasterizerStorageCommon::BatchFlags
|
||||
unsigned int batch_flags;
|
||||
|
||||
bool uses_screen_texture;
|
||||
|
|
|
@ -19,7 +19,7 @@ uniform highp mat4 modelview_matrix;
|
|||
uniform highp mat4 extra_matrix;
|
||||
attribute highp vec2 vertex; // attrib:0
|
||||
|
||||
#ifdef USE_LIGHT_ANGLE
|
||||
#ifdef USE_ATTRIB_LIGHT_ANGLE
|
||||
// shared with tangent, not used in canvas shader
|
||||
attribute highp float light_angle; // attrib:2
|
||||
#endif
|
||||
|
@ -27,6 +27,16 @@ attribute highp float light_angle; // attrib:2
|
|||
attribute vec4 color_attrib; // attrib:3
|
||||
attribute vec2 uv_attrib; // attrib:4
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
attribute highp vec4 modulate_attrib; // attrib:5
|
||||
#endif
|
||||
|
||||
#ifdef USE_ATTRIB_LARGE_VERTEX
|
||||
// shared with skeleton attributes, not used in batched shader
|
||||
attribute highp vec2 translate_attrib; // attrib:6
|
||||
attribute highp vec4 basis_attrib; // attrib:7
|
||||
#endif
|
||||
|
||||
#ifdef USE_SKELETON
|
||||
attribute highp vec4 bone_indices; // attrib:6
|
||||
attribute highp vec4 bone_weights; // attrib:7
|
||||
|
@ -54,6 +64,12 @@ uniform highp mat4 skeleton_transform_inverse;
|
|||
|
||||
varying vec2 uv_interp;
|
||||
varying vec4 color_interp;
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
// modulate doesn't need interpolating but we need to send it to the fragment shader
|
||||
varying vec4 modulate_interp;
|
||||
#endif
|
||||
|
||||
#ifdef MODULATE_USED
|
||||
uniform vec4 final_modulate;
|
||||
#endif
|
||||
|
@ -171,6 +187,24 @@ VERTEX_SHADER_CODE
|
|||
|
||||
gl_PointSize = point_size;
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
// modulate doesn't need interpolating but we need to send it to the fragment shader
|
||||
modulate_interp = modulate_attrib;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ATTRIB_LARGE_VERTEX
|
||||
// transform is in attributes
|
||||
vec2 temp;
|
||||
|
||||
temp = outvec.xy;
|
||||
temp.x = (outvec.x * basis_attrib.x) + (outvec.y * basis_attrib.z);
|
||||
temp.y = (outvec.x * basis_attrib.y) + (outvec.y * basis_attrib.w);
|
||||
|
||||
temp += translate_attrib;
|
||||
outvec.xy = temp;
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(SKIP_TRANSFORM_USED)
|
||||
outvec = extra_matrix_instance * outvec;
|
||||
outvec = modelview_matrix * outvec;
|
||||
|
@ -225,7 +259,7 @@ VERTEX_SHADER_CODE
|
|||
pos = outvec.xy;
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHT_ANGLE
|
||||
#ifdef USE_ATTRIB_LIGHT_ANGLE
|
||||
// we add a fixed offset because we are using the sign later,
|
||||
// and don't want floating point error around 0.0
|
||||
float la = abs(light_angle) - 1.0;
|
||||
|
@ -303,6 +337,10 @@ uniform mediump sampler2D normal_texture; // texunit:-2
|
|||
varying mediump vec2 uv_interp;
|
||||
varying mediump vec4 color_interp;
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
varying mediump vec4 modulate_interp;
|
||||
#endif
|
||||
|
||||
uniform highp float time;
|
||||
|
||||
uniform vec4 final_modulate;
|
||||
|
@ -442,6 +480,11 @@ FRAGMENT_SHADER_CODE
|
|||
color *= final_modulate;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
// todo .. this won't be used at the same time as MODULATE_USED
|
||||
color *= modulate_interp;
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHTING
|
||||
|
||||
vec2 light_vec = transformed_light_uv;
|
||||
|
|
1321
drivers/gles3/rasterizer_canvas_base_gles3.cpp
Normal file
1321
drivers/gles3/rasterizer_canvas_base_gles3.cpp
Normal file
File diff suppressed because it is too large
Load diff
163
drivers/gles3/rasterizer_canvas_base_gles3.h
Normal file
163
drivers/gles3/rasterizer_canvas_base_gles3.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*************************************************************************/
|
||||
/* rasterizer_canvas_base_gles3.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef RASTERIZERCANVASBASEGLES3_H
|
||||
#define RASTERIZERCANVASBASEGLES3_H
|
||||
|
||||
#include "rasterizer_storage_gles3.h"
|
||||
#include "servers/visual/rasterizer.h"
|
||||
|
||||
#include "shaders/canvas_shadow.glsl.gen.h"
|
||||
#include "shaders/lens_distorted.glsl.gen.h"
|
||||
|
||||
class RasterizerSceneGLES3;
|
||||
|
||||
class RasterizerCanvasBaseGLES3 : public RasterizerCanvas {
|
||||
public:
|
||||
struct CanvasItemUBO {
|
||||
|
||||
float projection_matrix[16];
|
||||
float time;
|
||||
uint8_t padding[12];
|
||||
};
|
||||
|
||||
RasterizerSceneGLES3 *scene_render;
|
||||
|
||||
struct Data {
|
||||
|
||||
enum { NUM_QUAD_ARRAY_VARIATIONS = 8 };
|
||||
|
||||
GLuint canvas_quad_vertices;
|
||||
GLuint canvas_quad_array;
|
||||
|
||||
GLuint polygon_buffer;
|
||||
GLuint polygon_buffer_quad_arrays[NUM_QUAD_ARRAY_VARIATIONS];
|
||||
GLuint polygon_buffer_pointer_array;
|
||||
GLuint polygon_index_buffer;
|
||||
|
||||
GLuint particle_quad_vertices;
|
||||
GLuint particle_quad_array;
|
||||
|
||||
uint32_t polygon_buffer_size;
|
||||
uint32_t polygon_index_buffer_size;
|
||||
|
||||
} data;
|
||||
|
||||
struct State {
|
||||
CanvasItemUBO canvas_item_ubo_data;
|
||||
GLuint canvas_item_ubo;
|
||||
bool canvas_texscreen_used;
|
||||
CanvasShaderGLES3 canvas_shader;
|
||||
CanvasShadowShaderGLES3 canvas_shadow_shader;
|
||||
LensDistortedShaderGLES3 lens_shader;
|
||||
|
||||
bool using_texture_rect;
|
||||
bool using_ninepatch;
|
||||
|
||||
bool using_light_angle;
|
||||
bool using_modulate;
|
||||
bool using_large_vertex;
|
||||
|
||||
RID current_tex;
|
||||
RID current_normal;
|
||||
RasterizerStorageGLES3::Texture *current_tex_ptr;
|
||||
|
||||
Transform vp;
|
||||
|
||||
Color canvas_item_modulate;
|
||||
Transform2D extra_matrix;
|
||||
Transform2D final_transform;
|
||||
bool using_skeleton;
|
||||
Transform2D skeleton_transform;
|
||||
Transform2D skeleton_transform_inverse;
|
||||
|
||||
} state;
|
||||
|
||||
RasterizerStorageGLES3 *storage;
|
||||
|
||||
struct LightInternal : public RID_Data {
|
||||
|
||||
struct UBOData {
|
||||
|
||||
float light_matrix[16];
|
||||
float local_matrix[16];
|
||||
float shadow_matrix[16];
|
||||
float color[4];
|
||||
float shadow_color[4];
|
||||
float light_pos[2];
|
||||
float shadowpixel_size;
|
||||
float shadow_gradient;
|
||||
float light_height;
|
||||
float light_outside_alpha;
|
||||
float shadow_distance_mult;
|
||||
uint8_t padding[4];
|
||||
} ubo_data;
|
||||
|
||||
GLuint ubo;
|
||||
};
|
||||
|
||||
RID_Owner<LightInternal> light_internal_owner;
|
||||
|
||||
virtual RID light_internal_create();
|
||||
virtual void light_internal_update(RID p_rid, Light *p_light);
|
||||
virtual void light_internal_free(RID p_rid);
|
||||
|
||||
virtual void canvas_begin();
|
||||
virtual void canvas_end();
|
||||
|
||||
void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false, bool p_light_angle = false, bool p_modulate = false, bool p_large_vertex = false);
|
||||
RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force = false);
|
||||
|
||||
void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs, const float *p_light_angles = nullptr);
|
||||
void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights);
|
||||
void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
|
||||
void _draw_generic_indices(GLuint p_primitive, const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
|
||||
|
||||
void _copy_texscreen(const Rect2 &p_rect);
|
||||
|
||||
virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow);
|
||||
|
||||
virtual void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache);
|
||||
|
||||
virtual void reset_canvas();
|
||||
|
||||
void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src);
|
||||
void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
|
||||
void render_rect_nvidia_workaround(const Item::CommandRect *p_rect, const RasterizerStorageGLES3::Texture *p_texture);
|
||||
|
||||
void initialize();
|
||||
void finalize();
|
||||
|
||||
virtual void draw_window_margins(int *black_margin, RID *black_image);
|
||||
|
||||
RasterizerCanvasBaseGLES3();
|
||||
};
|
||||
|
||||
#endif // RASTERIZERCANVASBASEGLES3_H
|
File diff suppressed because it is too large
Load diff
|
@ -31,132 +31,49 @@
|
|||
#ifndef RASTERIZERCANVASGLES3_H
|
||||
#define RASTERIZERCANVASGLES3_H
|
||||
|
||||
#include "rasterizer_storage_gles3.h"
|
||||
#include "servers/visual/rasterizer.h"
|
||||
#include "drivers/gles_common/rasterizer_canvas_batcher.h"
|
||||
#include "rasterizer_canvas_base_gles3.h"
|
||||
|
||||
#include "shaders/canvas_shadow.glsl.gen.h"
|
||||
#include "shaders/lens_distorted.glsl.gen.h"
|
||||
class RasterizerCanvasGLES3 : public RasterizerCanvasBaseGLES3, public RasterizerCanvasBatcher<RasterizerCanvasGLES3, RasterizerStorageGLES3> {
|
||||
friend class RasterizerCanvasBatcher<RasterizerCanvasGLES3, RasterizerStorageGLES3>;
|
||||
|
||||
class RasterizerSceneGLES3;
|
||||
private:
|
||||
struct BatchGLData {
|
||||
// for batching
|
||||
GLuint batch_vertex_array[5];
|
||||
} batch_gl_data;
|
||||
|
||||
class RasterizerCanvasGLES3 : public RasterizerCanvas {
|
||||
public:
|
||||
struct CanvasItemUBO {
|
||||
|
||||
float projection_matrix[16];
|
||||
float time;
|
||||
uint8_t padding[12];
|
||||
};
|
||||
|
||||
RasterizerSceneGLES3 *scene_render;
|
||||
|
||||
struct Data {
|
||||
|
||||
enum { NUM_QUAD_ARRAY_VARIATIONS = 8 };
|
||||
|
||||
GLuint canvas_quad_vertices;
|
||||
GLuint canvas_quad_array;
|
||||
|
||||
GLuint polygon_buffer;
|
||||
GLuint polygon_buffer_quad_arrays[NUM_QUAD_ARRAY_VARIATIONS];
|
||||
GLuint polygon_buffer_pointer_array;
|
||||
GLuint polygon_index_buffer;
|
||||
|
||||
GLuint particle_quad_vertices;
|
||||
GLuint particle_quad_array;
|
||||
|
||||
uint32_t polygon_buffer_size;
|
||||
uint32_t polygon_index_buffer_size;
|
||||
|
||||
} data;
|
||||
|
||||
struct State {
|
||||
CanvasItemUBO canvas_item_ubo_data;
|
||||
GLuint canvas_item_ubo;
|
||||
bool canvas_texscreen_used;
|
||||
CanvasShaderGLES3 canvas_shader;
|
||||
CanvasShadowShaderGLES3 canvas_shadow_shader;
|
||||
LensDistortedShaderGLES3 lens_shader;
|
||||
|
||||
bool using_texture_rect;
|
||||
bool using_ninepatch;
|
||||
bool using_light_angle;
|
||||
|
||||
RID current_tex;
|
||||
RID current_normal;
|
||||
RasterizerStorageGLES3::Texture *current_tex_ptr;
|
||||
|
||||
Transform vp;
|
||||
|
||||
Color canvas_item_modulate;
|
||||
Transform2D extra_matrix;
|
||||
Transform2D final_transform;
|
||||
bool using_skeleton;
|
||||
Transform2D skeleton_transform;
|
||||
Transform2D skeleton_transform_inverse;
|
||||
|
||||
} state;
|
||||
|
||||
RasterizerStorageGLES3 *storage;
|
||||
bool use_nvidia_rect_workaround;
|
||||
|
||||
struct LightInternal : public RID_Data {
|
||||
|
||||
struct UBOData {
|
||||
|
||||
float light_matrix[16];
|
||||
float local_matrix[16];
|
||||
float shadow_matrix[16];
|
||||
float color[4];
|
||||
float shadow_color[4];
|
||||
float light_pos[2];
|
||||
float shadowpixel_size;
|
||||
float shadow_gradient;
|
||||
float light_height;
|
||||
float light_outside_alpha;
|
||||
float shadow_distance_mult;
|
||||
uint8_t padding[4];
|
||||
} ubo_data;
|
||||
|
||||
GLuint ubo;
|
||||
};
|
||||
|
||||
RID_Owner<LightInternal> light_internal_owner;
|
||||
|
||||
virtual RID light_internal_create();
|
||||
virtual void light_internal_update(RID p_rid, Light *p_light);
|
||||
virtual void light_internal_free(RID p_rid);
|
||||
|
||||
virtual void canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
|
||||
virtual void canvas_render_items_end();
|
||||
virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
|
||||
virtual void canvas_begin();
|
||||
virtual void canvas_end();
|
||||
|
||||
_FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false, bool p_light_angle = false);
|
||||
_FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force = false);
|
||||
private:
|
||||
// legacy codepath .. to remove after testing
|
||||
void _legacy_canvas_render_item(Item *p_ci, RenderItemState &r_ris);
|
||||
|
||||
_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs, const float *p_light_angles = nullptr);
|
||||
_FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights);
|
||||
_FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
|
||||
_FORCE_INLINE_ void _draw_generic_indices(GLuint p_primitive, const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
|
||||
// high level batch funcs
|
||||
void canvas_render_items_implementation(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
|
||||
void render_joined_item(const BItemJoined &p_bij, RenderItemState &r_ris);
|
||||
bool try_join_item(Item *p_ci, RenderItemState &r_ris, bool &r_batch_break);
|
||||
void render_batches(Item::Command *const *p_commands, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES3::Material *p_material);
|
||||
|
||||
_FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip);
|
||||
_FORCE_INLINE_ void _copy_texscreen(const Rect2 &p_rect);
|
||||
// low level batch funcs
|
||||
void _batch_upload_buffers();
|
||||
void _batch_render_rects(const Batch &p_batch, RasterizerStorageGLES3::Material *p_material);
|
||||
void _batch_render_polys(const Batch &p_batch, RasterizerStorageGLES3::Material *p_material);
|
||||
void _batch_render_lines(const Batch &p_batch, RasterizerStorageGLES3::Material *p_material, bool p_anti_alias);
|
||||
|
||||
virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_transform);
|
||||
virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow);
|
||||
// funcs used from rasterizer_canvas_batcher template
|
||||
void gl_enable_scissor(int p_x, int p_y, int p_width, int p_height) const;
|
||||
void gl_disable_scissor() const;
|
||||
|
||||
virtual void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache);
|
||||
|
||||
virtual void reset_canvas();
|
||||
|
||||
void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src);
|
||||
void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
|
||||
void render_rect_nvidia_workaround(const Item::CommandRect *p_rect, const RasterizerStorageGLES3::Texture *p_texture);
|
||||
void gl_checkerror();
|
||||
|
||||
public:
|
||||
void initialize();
|
||||
void finalize();
|
||||
|
||||
virtual void draw_window_margins(int *black_margin, RID *black_image);
|
||||
|
||||
RasterizerCanvasGLES3();
|
||||
};
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ public:
|
|||
|
||||
virtual bool is_low_end() const { return false; }
|
||||
|
||||
const char *gl_check_for_error(bool p_print_error = true);
|
||||
virtual const char *gl_check_for_error(bool p_print_error = true);
|
||||
|
||||
RasterizerGLES3();
|
||||
~RasterizerGLES3();
|
||||
|
|
|
@ -2305,6 +2305,10 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
|
|||
p_shader->canvas_item.uses_screen_texture = false;
|
||||
p_shader->canvas_item.uses_screen_uv = false;
|
||||
p_shader->canvas_item.uses_time = false;
|
||||
p_shader->canvas_item.uses_modulate = false;
|
||||
p_shader->canvas_item.uses_color = false;
|
||||
p_shader->canvas_item.uses_vertex = false;
|
||||
p_shader->canvas_item.batch_flags = 0;
|
||||
|
||||
shaders.actions_canvas.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_ADD);
|
||||
shaders.actions_canvas.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MIX);
|
||||
|
@ -2321,6 +2325,10 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
|
|||
shaders.actions_canvas.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->canvas_item.uses_screen_texture;
|
||||
shaders.actions_canvas.usage_flag_pointers["TIME"] = &p_shader->canvas_item.uses_time;
|
||||
|
||||
shaders.actions_canvas.usage_flag_pointers["MODULATE"] = &p_shader->canvas_item.uses_modulate;
|
||||
shaders.actions_canvas.usage_flag_pointers["COLOR"] = &p_shader->canvas_item.uses_color;
|
||||
shaders.actions_canvas.usage_flag_pointers["VERTEX"] = &p_shader->canvas_item.uses_vertex;
|
||||
|
||||
actions = &shaders.actions_canvas;
|
||||
actions->uniforms = &p_shader->uniforms;
|
||||
|
||||
|
@ -2417,6 +2425,16 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
|
|||
p_shader->uses_vertex_time = gen_code.uses_vertex_time;
|
||||
p_shader->uses_fragment_time = gen_code.uses_fragment_time;
|
||||
|
||||
// some logic for batching
|
||||
if (p_shader->mode == VS::SHADER_CANVAS_ITEM) {
|
||||
if (p_shader->canvas_item.uses_modulate | p_shader->canvas_item.uses_color) {
|
||||
p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_COLOR_BAKING;
|
||||
}
|
||||
if (p_shader->canvas_item.uses_vertex) {
|
||||
p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_VERTEX_BAKING;
|
||||
}
|
||||
}
|
||||
|
||||
//all materials using this shader will have to be invalidated, unfortunately
|
||||
|
||||
for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) {
|
||||
|
@ -5067,7 +5085,8 @@ void RasterizerStorageGLES3::update_dirty_multimeshes() {
|
|||
if (multimesh->size && multimesh->dirty_data) {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, multimesh->data.size() * sizeof(float), multimesh->data.ptr());
|
||||
uint32_t buffer_size = multimesh->data.size() * sizeof(float);
|
||||
glBufferData(GL_ARRAY_BUFFER, buffer_size, multimesh->data.ptr(), GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
|
@ -8313,6 +8332,9 @@ void RasterizerStorageGLES3::initialize() {
|
|||
|
||||
#endif
|
||||
|
||||
// not yet detected on GLES3 (is this mandated?)
|
||||
config.support_npot_repeat_mipmap = true;
|
||||
|
||||
config.pvrtc_supported = config.extensions.has("GL_IMG_texture_compression_pvrtc");
|
||||
config.srgb_decode_supported = config.extensions.has("GL_EXT_texture_sRGB_decode");
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ public:
|
|||
|
||||
bool srgb_decode_supported;
|
||||
|
||||
bool support_npot_repeat_mipmap;
|
||||
bool texture_float_linear_supported;
|
||||
bool framebuffer_float_supported;
|
||||
bool framebuffer_half_float_supported;
|
||||
|
@ -455,9 +456,18 @@ public:
|
|||
|
||||
int light_mode;
|
||||
|
||||
// these flags are specifically for batching
|
||||
// some of the logic is thus in rasterizer_storage.cpp
|
||||
// we could alternatively set bitflags for each 'uses' and test on the fly
|
||||
// defined in RasterizerStorageCommon::BatchFlags
|
||||
unsigned int batch_flags;
|
||||
|
||||
bool uses_screen_texture;
|
||||
bool uses_screen_uv;
|
||||
bool uses_time;
|
||||
bool uses_modulate;
|
||||
bool uses_color;
|
||||
bool uses_vertex;
|
||||
|
||||
} canvas_item;
|
||||
|
||||
|
|
|
@ -3,13 +3,23 @@
|
|||
|
||||
layout(location = 0) in highp vec2 vertex;
|
||||
|
||||
#ifdef USE_LIGHT_ANGLE
|
||||
#ifdef USE_ATTRIB_LIGHT_ANGLE
|
||||
layout(location = 2) in highp float light_angle;
|
||||
#endif
|
||||
|
||||
/* clang-format on */
|
||||
layout(location = 3) in vec4 color_attrib;
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
layout(location = 5) in vec4 modulate_attrib; // attrib:5
|
||||
#endif
|
||||
|
||||
#ifdef USE_ATTRIB_LARGE_VERTEX
|
||||
// shared with skeleton attributes, not used in batched shader
|
||||
layout(location = 6) in vec2 translate_attrib; // attrib:6
|
||||
layout(location = 7) in vec4 basis_attrib; // attrib:7
|
||||
#endif
|
||||
|
||||
#ifdef USE_SKELETON
|
||||
layout(location = 6) in uvec4 bone_indices; // attrib:6
|
||||
layout(location = 7) in vec4 bone_weights; // attrib:7
|
||||
|
@ -53,6 +63,12 @@ uniform highp mat4 extra_matrix;
|
|||
|
||||
out highp vec2 uv_interp;
|
||||
out mediump vec4 color_interp;
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
// modulate doesn't need interpolating but we need to send it to the fragment shader
|
||||
out mediump vec4 modulate_interp;
|
||||
#endif
|
||||
|
||||
#ifdef MODULATE_USED
|
||||
uniform mediump vec4 final_modulate;
|
||||
#endif
|
||||
|
@ -177,6 +193,23 @@ VERTEX_SHADER_CODE
|
|||
pixel_size_interp = abs(dst_rect.zw) * vertex;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
// modulate doesn't need interpolating but we need to send it to the fragment shader
|
||||
modulate_interp = modulate_attrib;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ATTRIB_LARGE_VERTEX
|
||||
// transform is in attributes
|
||||
vec2 temp;
|
||||
|
||||
temp = outvec.xy;
|
||||
temp.x = (outvec.x * basis_attrib.x) + (outvec.y * basis_attrib.z);
|
||||
temp.y = (outvec.x * basis_attrib.y) + (outvec.y * basis_attrib.w);
|
||||
|
||||
temp += translate_attrib;
|
||||
outvec.xy = temp;
|
||||
#endif
|
||||
|
||||
#if !defined(SKIP_TRANSFORM_USED)
|
||||
outvec = extra_matrix * outvec;
|
||||
outvec = modelview_matrix * outvec;
|
||||
|
@ -253,7 +286,7 @@ VERTEX_SHADER_CODE
|
|||
pos = outvec.xy;
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHT_ANGLE
|
||||
#ifdef USE_ATTRIB_LIGHT_ANGLE
|
||||
// we add a fixed offset because we are using the sign later,
|
||||
// and don't want floating point error around 0.0
|
||||
float la = abs(light_angle) - 1.0;
|
||||
|
@ -294,6 +327,10 @@ uniform mediump sampler2D normal_texture; // texunit:1
|
|||
in highp vec2 uv_interp;
|
||||
in mediump vec4 color_interp;
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
in mediump vec4 modulate_interp;
|
||||
#endif
|
||||
|
||||
#if defined(SCREEN_TEXTURE_USED)
|
||||
|
||||
uniform sampler2D screen_texture; // texunit:-3
|
||||
|
@ -410,16 +447,14 @@ uniform bool np_draw_center;
|
|||
// left top right bottom in pixel coordinates
|
||||
uniform vec4 np_margins;
|
||||
|
||||
float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, float s_ratio, int np_repeat, inout int draw_center) {
|
||||
float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) {
|
||||
|
||||
float tex_size = 1.0 / tex_pixel_size;
|
||||
|
||||
float screen_margin_begin = margin_begin / s_ratio;
|
||||
float screen_margin_end = margin_end / s_ratio;
|
||||
if (pixel < screen_margin_begin) {
|
||||
return pixel * s_ratio * tex_pixel_size;
|
||||
} else if (pixel >= draw_size - screen_margin_end) {
|
||||
return (tex_size - (draw_size - pixel) * s_ratio) * tex_pixel_size;
|
||||
if (pixel < margin_begin) {
|
||||
return pixel * tex_pixel_size;
|
||||
} else if (pixel >= draw_size - margin_end) {
|
||||
return (tex_size - (draw_size - pixel)) * tex_pixel_size;
|
||||
} else {
|
||||
if (!np_draw_center) {
|
||||
draw_center--;
|
||||
|
@ -428,21 +463,21 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo
|
|||
// np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum.
|
||||
if (np_repeat == 0) { // Stretch.
|
||||
// Convert to ratio.
|
||||
float ratio = (pixel - screen_margin_begin) / (draw_size - screen_margin_begin - screen_margin_end);
|
||||
float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
|
||||
// Scale to source texture.
|
||||
return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
|
||||
} else if (np_repeat == 1) { // Tile.
|
||||
// Convert to offset.
|
||||
float ofs = mod((pixel - screen_margin_begin), tex_size - margin_begin - margin_end);
|
||||
float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
|
||||
// Scale to source texture.
|
||||
return (margin_begin + ofs) * tex_pixel_size;
|
||||
} else if (np_repeat == 2) { // Tile Fit.
|
||||
// Calculate scale.
|
||||
float src_area = draw_size - screen_margin_begin - screen_margin_end;
|
||||
float src_area = draw_size - margin_begin - margin_end;
|
||||
float dst_area = tex_size - margin_begin - margin_end;
|
||||
float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));
|
||||
// Convert to ratio.
|
||||
float ratio = (pixel - screen_margin_begin) / src_area;
|
||||
float ratio = (pixel - margin_begin) / src_area;
|
||||
ratio = mod(ratio * scale, 1.0);
|
||||
// Scale to source texture.
|
||||
return (margin_begin + ratio * dst_area) * tex_pixel_size;
|
||||
|
@ -467,11 +502,9 @@ void main() {
|
|||
#ifdef USE_NINEPATCH
|
||||
|
||||
int draw_center = 2;
|
||||
float s_ratio = max((1.0 / color_texpixel_size.x) / abs(dst_rect.z), (1.0 / color_texpixel_size.y) / abs(dst_rect.w));
|
||||
s_ratio = max(1.0, s_ratio);
|
||||
uv = vec2(
|
||||
map_ninepatch_axis(pixel_size_interp.x, abs(dst_rect.z), color_texpixel_size.x, np_margins.x, np_margins.z, s_ratio, np_repeat_h, draw_center),
|
||||
map_ninepatch_axis(pixel_size_interp.y, abs(dst_rect.w), color_texpixel_size.y, np_margins.y, np_margins.w, s_ratio, np_repeat_v, draw_center));
|
||||
map_ninepatch_axis(pixel_size_interp.x, abs(dst_rect.z), color_texpixel_size.x, np_margins.x, np_margins.z, np_repeat_h, draw_center),
|
||||
map_ninepatch_axis(pixel_size_interp.y, abs(dst_rect.w), color_texpixel_size.y, np_margins.y, np_margins.w, np_repeat_v, draw_center));
|
||||
|
||||
if (draw_center == 0) {
|
||||
color.a = 0.0;
|
||||
|
@ -549,6 +582,11 @@ FRAGMENT_SHADER_CODE
|
|||
color *= final_modulate;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ATTRIB_MODULATE
|
||||
// todo .. this won't be used at the same time as MODULATE_USED
|
||||
color *= modulate_interp;
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHTING
|
||||
|
||||
vec2 light_vec = transformed_light_uv;
|
||||
|
|
5
drivers/gles_common/SCsub
Normal file
5
drivers/gles_common/SCsub
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
148
drivers/gles_common/batch_diagnose.inc
Normal file
148
drivers/gles_common/batch_diagnose.inc
Normal file
|
@ -0,0 +1,148 @@
|
|||
String get_command_type_string(const RasterizerCanvas::Item::Command &p_command) const {
|
||||
String sz = "";
|
||||
|
||||
switch (p_command.type) {
|
||||
default:
|
||||
break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_LINE: {
|
||||
sz = "l";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_POLYLINE: {
|
||||
sz = "PL";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_RECT: {
|
||||
sz = "r";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_NINEPATCH: {
|
||||
sz = "n";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_PRIMITIVE: {
|
||||
sz = "PR";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_POLYGON: {
|
||||
sz = "p";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_MESH: {
|
||||
sz = "m";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_MULTIMESH: {
|
||||
sz = "MM";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_PARTICLES: {
|
||||
sz = "PA";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_CIRCLE: {
|
||||
sz = "c";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_TRANSFORM: {
|
||||
sz = "t";
|
||||
|
||||
// add a bit more info in debug build
|
||||
const RasterizerCanvas::Item::CommandTransform *transform = static_cast<const RasterizerCanvas::Item::CommandTransform *>(&p_command);
|
||||
const Transform2D &mat = transform->xform;
|
||||
|
||||
sz += " ";
|
||||
sz += String(Variant(mat.elements[2]));
|
||||
sz += " ";
|
||||
} break;
|
||||
case RasterizerCanvas::Item::Command::TYPE_CLIP_IGNORE: {
|
||||
sz = "CI";
|
||||
} break;
|
||||
} // switch
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
void diagnose_batches(RasterizerCanvas::Item::Command *const *p_commands) {
|
||||
int num_batches = bdata.batches.size();
|
||||
|
||||
BatchColor curr_color;
|
||||
curr_color.set(Color(-1, -1, -1, -1));
|
||||
bool first_color_change = true;
|
||||
|
||||
for (int batch_num = 0; batch_num < num_batches; batch_num++) {
|
||||
const Batch &batch = bdata.batches[batch_num];
|
||||
bdata.frame_string += "\t\t\tbatch ";
|
||||
|
||||
switch (batch.type) {
|
||||
|
||||
case RasterizerStorageCommon::BT_POLY: {
|
||||
bdata.frame_string += "P ";
|
||||
bdata.frame_string += itos(batch.first_command) + "-";
|
||||
bdata.frame_string += itos(batch.num_commands);
|
||||
|
||||
bdata.frame_string += " " + batch.color.to_string();
|
||||
|
||||
if (batch.num_commands > 1) {
|
||||
bdata.frame_string += " MULTI";
|
||||
}
|
||||
if (curr_color != batch.color) {
|
||||
curr_color = batch.color;
|
||||
if (!first_color_change) {
|
||||
bdata.frame_string += " color";
|
||||
} else {
|
||||
first_color_change = false;
|
||||
}
|
||||
}
|
||||
bdata.frame_string += "\n";
|
||||
} break;
|
||||
case RasterizerStorageCommon::BT_LINE:
|
||||
case RasterizerStorageCommon::BT_LINE_AA: {
|
||||
bdata.frame_string += "L ";
|
||||
bdata.frame_string += itos(batch.first_command) + "-";
|
||||
bdata.frame_string += itos(batch.num_commands);
|
||||
|
||||
bdata.frame_string += " " + batch.color.to_string();
|
||||
|
||||
if (batch.num_commands > 1) {
|
||||
bdata.frame_string += " MULTI";
|
||||
}
|
||||
if (curr_color != batch.color) {
|
||||
curr_color = batch.color;
|
||||
if (!first_color_change) {
|
||||
bdata.frame_string += " color";
|
||||
} else {
|
||||
first_color_change = false;
|
||||
}
|
||||
}
|
||||
bdata.frame_string += "\n";
|
||||
} break;
|
||||
case RasterizerStorageCommon::BT_RECT: {
|
||||
bdata.frame_string += "R ";
|
||||
bdata.frame_string += itos(batch.first_command) + "-";
|
||||
bdata.frame_string += itos(batch.num_commands);
|
||||
|
||||
int tex_id = (int)bdata.batch_textures[batch.batch_texture_id].RID_texture.get_id();
|
||||
bdata.frame_string += " [" + itos(batch.batch_texture_id) + " - " + itos(tex_id) + "]";
|
||||
|
||||
bdata.frame_string += " " + batch.color.to_string();
|
||||
|
||||
if (batch.num_commands > 1) {
|
||||
bdata.frame_string += " MULTI";
|
||||
}
|
||||
if (curr_color != batch.color) {
|
||||
curr_color = batch.color;
|
||||
if (!first_color_change) {
|
||||
bdata.frame_string += " color";
|
||||
} else {
|
||||
first_color_change = false;
|
||||
}
|
||||
}
|
||||
bdata.frame_string += "\n";
|
||||
} break;
|
||||
default: {
|
||||
bdata.frame_string += "D ";
|
||||
bdata.frame_string += itos(batch.first_command) + "-";
|
||||
bdata.frame_string += itos(batch.num_commands) + " ";
|
||||
|
||||
int num_show = MIN(batch.num_commands, 16);
|
||||
for (int n = 0; n < num_show; n++) {
|
||||
const RasterizerCanvas::Item::Command &comm = *p_commands[batch.first_command + n];
|
||||
bdata.frame_string += get_command_type_string(comm) + " ";
|
||||
}
|
||||
|
||||
bdata.frame_string += "\n";
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*************************************************************************/
|
||||
/* rasterizer_array_gles2.h */
|
||||
/* rasterizer_array.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
|
@ -30,36 +30,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
/*************************************************************************/
|
||||
/* rasterizer_array_gles2.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
* Fast single-threaded growable array for POD types.
|
||||
* For use in render drivers, not for general use.
|
||||
|
@ -141,14 +111,14 @@ private:
|
|||
};
|
||||
|
||||
template <class T>
|
||||
class RasterizerArrayGLES2 {
|
||||
class RasterizerArray {
|
||||
public:
|
||||
RasterizerArrayGLES2() {
|
||||
RasterizerArray() {
|
||||
_list = 0;
|
||||
_size = 0;
|
||||
_max_size = 0;
|
||||
}
|
||||
~RasterizerArrayGLES2() { free(); }
|
||||
~RasterizerArray() { free(); }
|
||||
|
||||
T &operator[](unsigned int ui) { return _list[ui]; }
|
||||
const T &operator[](unsigned int ui) const { return _list[ui]; }
|
||||
|
@ -208,7 +178,7 @@ public:
|
|||
int max_size() const { return _max_size; }
|
||||
const T *get_data() const { return _list; }
|
||||
|
||||
bool copy_from(const RasterizerArrayGLES2<T> &o) {
|
||||
bool copy_from(const RasterizerArray<T> &o) {
|
||||
// no resizing done here, it should be done manually
|
||||
if (o.size() > _max_size)
|
||||
return false;
|
||||
|
@ -247,9 +217,9 @@ private:
|
|||
};
|
||||
|
||||
template <class T>
|
||||
class RasterizerArray_non_pod_GLES2 {
|
||||
class RasterizerArray_non_pod {
|
||||
public:
|
||||
RasterizerArray_non_pod_GLES2() {
|
||||
RasterizerArray_non_pod() {
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
|
@ -287,3 +257,72 @@ private:
|
|||
Vector<T> _list;
|
||||
int _size;
|
||||
};
|
||||
|
||||
// very simple non-growable array, that keeps track of the size of a 'unit'
|
||||
// which can be cast to whatever vertex format FVF required, and is initially
|
||||
// created with enough memory to hold the biggest FVF.
|
||||
// This allows multiple FVFs to use the same array.
|
||||
class RasterizerUnitArray {
|
||||
public:
|
||||
RasterizerUnitArray() {
|
||||
_list = nullptr;
|
||||
free();
|
||||
}
|
||||
~RasterizerUnitArray() { free(); }
|
||||
|
||||
uint8_t *get_unit(unsigned int ui) { return &_list[ui * _unit_size_bytes]; }
|
||||
const uint8_t *get_unit(unsigned int ui) const { return &_list[ui * _unit_size_bytes]; }
|
||||
|
||||
int size() const { return _size; }
|
||||
int max_size() const { return _max_size; }
|
||||
|
||||
void free() {
|
||||
if (_list) {
|
||||
memdelete_arr(_list);
|
||||
_list = 0;
|
||||
}
|
||||
_size = 0;
|
||||
_max_size = 0;
|
||||
_max_size_bytes = 0;
|
||||
_unit_size_bytes = 0;
|
||||
}
|
||||
|
||||
void create(int p_max_size_units, int p_max_unit_size_bytes) {
|
||||
free();
|
||||
|
||||
_max_unit_size_bytes = p_max_unit_size_bytes;
|
||||
_max_size = p_max_size_units;
|
||||
_max_size_bytes = p_max_size_units * p_max_unit_size_bytes;
|
||||
|
||||
if (_max_size_bytes) {
|
||||
_list = memnew_arr(uint8_t, _max_size_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
void prepare(int p_unit_size_bytes) {
|
||||
_unit_size_bytes = p_unit_size_bytes;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
// several items at a time
|
||||
uint8_t *request(int p_num_items = 1) {
|
||||
int old_size = _size;
|
||||
_size += p_num_items;
|
||||
|
||||
if (_size <= _max_size) {
|
||||
return get_unit(old_size);
|
||||
}
|
||||
|
||||
// revert
|
||||
_size = old_size;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t *_list;
|
||||
int _size; // in units
|
||||
int _max_size; // in units
|
||||
int _max_size_bytes;
|
||||
int _unit_size_bytes;
|
||||
int _max_unit_size_bytes;
|
||||
};
|
2926
drivers/gles_common/rasterizer_canvas_batcher.h
Normal file
2926
drivers/gles_common/rasterizer_canvas_batcher.h
Normal file
File diff suppressed because it is too large
Load diff
71
drivers/gles_common/rasterizer_storage_common.h
Normal file
71
drivers/gles_common/rasterizer_storage_common.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*************************************************************************/
|
||||
/* rasterizer_storage_common.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
class RasterizerStorageCommon {
|
||||
public:
|
||||
enum FVF {
|
||||
FVF_UNBATCHED,
|
||||
FVF_REGULAR,
|
||||
FVF_COLOR,
|
||||
FVF_LIGHT_ANGLE,
|
||||
FVF_MODULATED,
|
||||
FVF_LARGE,
|
||||
};
|
||||
|
||||
// these flags are specifically for batching
|
||||
// some of the logic is thus in rasterizer_storage.cpp
|
||||
// we could alternatively set bitflags for each 'uses' and test on the fly
|
||||
enum BatchFlags {
|
||||
PREVENT_COLOR_BAKING = 1 << 0,
|
||||
PREVENT_VERTEX_BAKING = 1 << 1,
|
||||
|
||||
USE_MODULATE_FVF = 1 << 2,
|
||||
USE_LARGE_FVF = 1 << 3,
|
||||
};
|
||||
|
||||
enum BatchType : uint16_t {
|
||||
BT_DEFAULT = 0,
|
||||
BT_RECT = 1,
|
||||
BT_LINE = 2,
|
||||
BT_LINE_AA = 3,
|
||||
BT_POLY = 4,
|
||||
BT_DUMMY = 5, // dummy batch is just used to keep the batch creation loop simple
|
||||
};
|
||||
|
||||
enum BatchTypeFlags {
|
||||
BTF_DEFAULT = 1 << BT_DEFAULT,
|
||||
BTF_RECT = 1 << BT_RECT,
|
||||
BTF_LINE = 1 << BT_LINE,
|
||||
BTF_LINE_AA = 1 << BT_LINE_AA,
|
||||
BTF_POLY = 1 << BT_POLY,
|
||||
};
|
||||
};
|
|
@ -53,6 +53,8 @@
|
|||
#include "scene/main/viewport.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
// Min and Max are power of two in order to play nicely with successive increment.
|
||||
// That way, we can naturally reach a 100% zoom from boundaries.
|
||||
#define MIN_ZOOM 1. / 128
|
||||
|
|
|
@ -116,6 +116,7 @@ def configure(env):
|
|||
env.Prepend(CCFLAGS=["-g2"])
|
||||
|
||||
elif env["target"] == "debug":
|
||||
env.Prepend(CCFLAGS=["-ggdb"])
|
||||
env.Prepend(CCFLAGS=["-g3"])
|
||||
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
|
||||
env.Append(LINKFLAGS=["-rdynamic"])
|
||||
|
|
|
@ -1180,6 +1180,8 @@ public:
|
|||
|
||||
virtual bool is_low_end() const = 0;
|
||||
|
||||
virtual const char *gl_check_for_error(bool p_print_error = true) = 0;
|
||||
|
||||
virtual ~Rasterizer() {}
|
||||
};
|
||||
|
||||
|
|
|
@ -2439,6 +2439,10 @@ VisualServer::VisualServer() {
|
|||
GLOBAL_DEF(sz_balance_render_tree, 0.17f);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info(sz_balance_render_tree, PropertyInfo(Variant::REAL, sz_balance_render_tree, PROPERTY_HINT_RANGE, "0.0,1.0,0.01"));
|
||||
|
||||
GLOBAL_DEF("rendering/quality/2d/use_software_skinning", true);
|
||||
GLOBAL_DEF("rendering/quality/2d/ninepatch_mode", 0);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/2d/ninepatch_mode", PropertyInfo(Variant::INT, "rendering/quality/2d/ninepatch_mode", PROPERTY_HINT_ENUM, "Default,Scaling"));
|
||||
|
||||
GLOBAL_DEF("rendering/batching/options/use_batching", true);
|
||||
GLOBAL_DEF_RST("rendering/batching/options/use_batching_in_editor", true);
|
||||
GLOBAL_DEF("rendering/batching/options/single_rect_fallback", false);
|
||||
|
|
Loading…
Reference in a new issue