Merge pull request #42119 from lawnjelly/ewok3

Unified GLES3 / GLES2 Batching
This commit is contained in:
Rémi Verschelde 2020-10-19 15:01:10 +02:00 committed by GitHub
commit 123942f61f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 7361 additions and 4495 deletions

View file

@ -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.

View file

@ -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")

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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();
};

View file

@ -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);

View file

@ -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;

View file

@ -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;

File diff suppressed because it is too large Load diff

View 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

View file

@ -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();
};

View file

@ -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();

View file

@ -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");

View file

@ -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;

View file

@ -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;

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.drivers_sources, "*.cpp")

View 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;
}
}
}

View file

@ -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;
};

File diff suppressed because it is too large Load diff

View 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,
};
};

View file

@ -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

View file

@ -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"])

View file

@ -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() {}
};

View file

@ -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);