godot/servers/visual/rasterizer/rasterizer_canvas_rd.cpp

2112 lines
73 KiB
C++
Raw Normal View History

#include "rasterizer_canvas_rd.h"
#include "core/math/math_funcs.h"
void RasterizerCanvasRD::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) {
p_mat4[0] = p_transform.elements[0][0];
p_mat4[1] = p_transform.elements[0][1];
p_mat4[2] = 0;
p_mat4[3] = 0;
p_mat4[4] = p_transform.elements[1][0];
p_mat4[5] = p_transform.elements[1][1];
p_mat4[6] = 0;
p_mat4[7] = 0;
p_mat4[8] = 0;
p_mat4[9] = 0;
p_mat4[10] = 1;
p_mat4[11] = 0;
p_mat4[12] = p_transform.elements[2][0];
p_mat4[13] = p_transform.elements[2][1];
p_mat4[14] = 0;
p_mat4[15] = 1;
}
void RasterizerCanvasRD::_update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4) {
p_mat2x4[0] = p_transform.elements[0][0];
p_mat2x4[1] = p_transform.elements[1][0];
p_mat2x4[2] = 0;
p_mat2x4[3] = p_transform.elements[2][0];
p_mat2x4[4] = p_transform.elements[0][1];
p_mat2x4[5] = p_transform.elements[1][1];
p_mat2x4[6] = 0;
p_mat2x4[7] = p_transform.elements[2][1];
}
void RasterizerCanvasRD::_update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3) {
p_mat2x3[0] = p_transform.elements[0][0];
p_mat2x3[1] = p_transform.elements[0][1];
p_mat2x3[2] = p_transform.elements[1][0];
p_mat2x3[3] = p_transform.elements[1][1];
p_mat2x3[4] = p_transform.elements[2][0];
p_mat2x3[5] = p_transform.elements[2][1];
}
void RasterizerCanvasRD::_update_transform_to_mat4(const Transform &p_transform, float *p_mat4) {
p_mat4[0] = p_transform.basis.elements[0][0];
p_mat4[1] = p_transform.basis.elements[1][0];
p_mat4[2] = p_transform.basis.elements[2][0];
p_mat4[3] = 0;
p_mat4[4] = p_transform.basis.elements[0][1];
p_mat4[5] = p_transform.basis.elements[1][1];
p_mat4[6] = p_transform.basis.elements[2][1];
p_mat4[7] = 0;
p_mat4[8] = p_transform.basis.elements[0][2];
p_mat4[9] = p_transform.basis.elements[1][2];
p_mat4[10] = p_transform.basis.elements[2][2];
p_mat4[11] = 0;
p_mat4[12] = p_transform.origin.x;
p_mat4[13] = p_transform.origin.y;
p_mat4[14] = p_transform.origin.z;
p_mat4[15] = 1;
}
void RasterizerCanvasRD::_update_specular_shininess(const Color &p_transform, uint32_t *r_ss) {
*r_ss = uint32_t(CLAMP(p_transform.a * 255.0, 0, 255)) << 24;
*r_ss |= uint32_t(CLAMP(p_transform.b * 255.0, 0, 255)) << 16;
*r_ss |= uint32_t(CLAMP(p_transform.g * 255.0, 0, 255)) << 8;
*r_ss |= uint32_t(CLAMP(p_transform.r * 255.0, 0, 255));
}
RID RasterizerCanvasRD::_create_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat, RID p_multimesh) {
Vector<RD::Uniform> uniform_set;
{ // COLOR TEXTURE
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1;
RID texture = storage->texture_get_rd_texture(p_texture);
if (!texture.is_valid()) {
//use default white texture
texture = default_textures.white_texture;
}
u.ids.push_back(texture);
uniform_set.push_back(u);
}
{ // NORMAL TEXTURE
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2;
RID texture = storage->texture_get_rd_texture(p_normalmap);
if (!texture.is_valid()) {
//use default normal texture
texture = default_textures.normal_texture;
}
u.ids.push_back(texture);
uniform_set.push_back(u);
}
{ // SPECULAR TEXTURE
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 3;
RID texture = storage->texture_get_rd_texture(p_specular);
if (!texture.is_valid()) {
//use default white texture
texture = default_textures.white_texture;
}
u.ids.push_back(texture);
uniform_set.push_back(u);
}
{ // SAMPLER
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_SAMPLER;
u.binding = 4;
RID sampler = default_samplers.samplers[p_filter][p_repeat];
ERR_FAIL_COND_V(sampler.is_null(), RID());
u.ids.push_back(sampler);
uniform_set.push_back(u);
}
{ // MULTIMESH TEXTURE BUFFER
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_TEXTURE_BUFFER;
u.binding = 5;
u.ids.push_back(default_textures.default_multimesh_tb);
uniform_set.push_back(u);
}
return RD::get_singleton()->uniform_set_create(uniform_set, shader.default_version_rd_shader, 0);
}
RasterizerCanvas::TextureBindingID RasterizerCanvasRD::request_texture_binding(RID p_texture, RID p_normalmap, RID p_specular, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat, RID p_multimesh) {
if (p_filter == VS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) {
p_filter = default_samplers.default_filter;
}
if (p_repeat == VS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) {
p_repeat = default_samplers.default_repeat;
}
TextureBindingKey key;
key.texture = p_texture;
key.normalmap = p_normalmap;
key.specular = p_specular;
key.multimesh = p_multimesh;
key.texture_filter = p_filter;
key.texture_repeat = p_repeat;
TextureBinding *binding;
TextureBindingID id;
{
TextureBindingID *idptr = bindings.texture_key_bindings.getptr(key);
if (!idptr) {
id = bindings.id_generator++;
bindings.texture_key_bindings[key] = id;
binding = memnew(TextureBinding);
binding->key = key;
binding->id = id;
bindings.texture_bindings[id] = binding;
} else {
id = *idptr;
binding = bindings.texture_bindings[id];
}
}
binding->reference_count++;
if (binding->to_dispose.in_list()) {
//was queued for disposal previously, but ended up reused.
bindings.to_dispose_list.remove(&binding->to_dispose);
}
if (binding->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(binding->uniform_set)) {
binding->uniform_set = _create_texture_binding(p_texture, p_normalmap, p_specular, p_filter, p_repeat, p_multimesh);
}
return id;
}
void RasterizerCanvasRD::free_texture_binding(TextureBindingID p_binding) {
TextureBinding **binding_ptr = bindings.texture_bindings.getptr(p_binding);
ERR_FAIL_COND(!binding_ptr);
TextureBinding *binding = *binding_ptr;
ERR_FAIL_COND(binding->reference_count == 0);
binding->reference_count--;
if (binding->reference_count == 0) {
bindings.to_dispose_list.add(&binding->to_dispose);
}
}
void RasterizerCanvasRD::_dispose_bindings() {
while (bindings.to_dispose_list.first()) {
TextureBinding *binding = bindings.to_dispose_list.first()->self();
if (binding->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(binding->uniform_set)) {
RD::get_singleton()->free(binding->uniform_set);
}
bindings.texture_key_bindings.erase(binding->key);
bindings.texture_bindings.erase(binding->id);
bindings.to_dispose_list.remove(&binding->to_dispose);
memdelete(binding);
}
}
RasterizerCanvas::PolygonID RasterizerCanvasRD::request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, const Vector<int> &p_bones, const Vector<float> &p_weights) {
// Care must be taken to generate array formats
// in ways where they could be reused, so we will
// put single-occuring elements first, and repeated
// elements later. This way the generated formats are
// the same no matter the length of the arrays.
// This dramatically reduces the amount of pipeline objects
// that need to be created for these formats.
uint32_t vertex_count = p_points.size();
uint32_t base_offset = 0;
uint32_t stride = 2; //vertices always repeat
if ((uint32_t)p_colors.size() == vertex_count) {
stride += 4;
} else {
base_offset += 4;
}
if ((uint32_t)p_uvs.size() == vertex_count) {
stride += 2;
} else {
base_offset += 2;
}
if ((uint32_t)p_bones.size() == vertex_count * 4) {
stride += 4;
} else {
base_offset += 4;
}
if ((uint32_t)p_weights.size() == vertex_count * 4) {
stride += 4;
} else {
base_offset += 4;
}
uint32_t buffer_size = base_offset + stride * p_points.size();
PoolVector<uint8_t> polygon_buffer;
polygon_buffer.resize(buffer_size * sizeof(float));
Vector<RD::VertexDescription> descriptions;
descriptions.resize(5);
{
PoolVector<uint8_t>::Read r = polygon_buffer.read();
float *fptr = (float *)r.ptr();
uint32_t *uptr = (uint32_t *)r.ptr();
uint32_t single_offset = 0;
{ //vertices
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
vd.offset = base_offset * sizeof(float);
vd.location = VS::ARRAY_VERTEX;
vd.stride = stride * sizeof(float);
descriptions.write[0] = vd;
const Vector2 *points_ptr = p_points.ptr();
for (uint32_t i = 0; i < vertex_count; i++) {
fptr[base_offset + i * stride + 0] = points_ptr[i].x;
fptr[base_offset + i * stride + 1] = points_ptr[i].y;
}
base_offset += 2;
}
//colors
if ((uint32_t)p_colors.size() == vertex_count) {
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
vd.offset = base_offset * sizeof(float);
vd.location = VS::ARRAY_COLOR;
vd.stride = stride * sizeof(float);
descriptions.write[1] = vd;
const Color *color_ptr = p_colors.ptr();
for (uint32_t i = 0; i < vertex_count; i++) {
fptr[base_offset + i * stride + 0] = color_ptr[i].r;
fptr[base_offset + i * stride + 1] = color_ptr[i].g;
fptr[base_offset + i * stride + 2] = color_ptr[i].b;
fptr[base_offset + i * stride + 3] = color_ptr[i].a;
}
base_offset += 4;
} else {
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
vd.offset = single_offset * sizeof(float);
vd.location = VS::ARRAY_COLOR;
vd.stride = 0;
descriptions.write[1] = vd;
Color color = p_colors.size() ? p_colors[0] : Color(1, 1, 1, 1);
fptr[single_offset + 0] = color.r;
fptr[single_offset + 1] = color.g;
fptr[single_offset + 2] = color.b;
fptr[single_offset + 3] = color.a;
single_offset += 4;
}
//uvs
if ((uint32_t)p_uvs.size() == vertex_count) {
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
vd.offset = base_offset * sizeof(float);
vd.location = VS::ARRAY_TEX_UV;
vd.stride = stride * sizeof(float);
descriptions.write[2] = vd;
const Vector2 *uv_ptr = p_uvs.ptr();
for (uint32_t i = 0; i < vertex_count; i++) {
fptr[base_offset + i * stride + 0] = uv_ptr[i].x;
fptr[base_offset + i * stride + 1] = uv_ptr[i].y;
}
base_offset += 2;
} else {
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
vd.offset = single_offset * sizeof(float);
vd.location = VS::ARRAY_TEX_UV;
vd.stride = 0;
descriptions.write[2] = vd;
Vector2 uv;
fptr[single_offset + 0] = uv.x;
fptr[single_offset + 1] = uv.y;
single_offset += 2;
}
//bones
if ((uint32_t)p_indices.size() == vertex_count * 4) {
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT;
vd.offset = base_offset * sizeof(float);
vd.location = VS::ARRAY_BONES;
vd.stride = stride * sizeof(float);
descriptions.write[3] = vd;
const int *bone_ptr = p_bones.ptr();
for (uint32_t i = 0; i < vertex_count; i++) {
uptr[base_offset + i * stride + 0] = bone_ptr[i * 4 + 0];
uptr[base_offset + i * stride + 1] = bone_ptr[i * 4 + 1];
uptr[base_offset + i * stride + 2] = bone_ptr[i * 4 + 2];
uptr[base_offset + i * stride + 3] = bone_ptr[i * 4 + 3];
}
base_offset += 4;
} else {
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT;
vd.offset = single_offset * sizeof(float);
vd.location = VS::ARRAY_BONES;
vd.stride = 0;
descriptions.write[3] = vd;
uptr[single_offset + 0] = 0;
uptr[single_offset + 1] = 0;
uptr[single_offset + 2] = 0;
uptr[single_offset + 3] = 0;
single_offset += 4;
}
//bones
if ((uint32_t)p_weights.size() == vertex_count * 4) {
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
vd.offset = base_offset * sizeof(float);
vd.location = VS::ARRAY_WEIGHTS;
vd.stride = stride * sizeof(float);
descriptions.write[4] = vd;
const float *weight_ptr = p_weights.ptr();
for (uint32_t i = 0; i < vertex_count; i++) {
fptr[base_offset + i * stride + 0] = weight_ptr[i * 4 + 0];
fptr[base_offset + i * stride + 1] = weight_ptr[i * 4 + 1];
fptr[base_offset + i * stride + 2] = weight_ptr[i * 4 + 2];
fptr[base_offset + i * stride + 3] = weight_ptr[i * 4 + 3];
}
base_offset += 4;
} else {
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
vd.offset = single_offset * sizeof(float);
vd.location = VS::ARRAY_WEIGHTS;
vd.stride = 0;
descriptions.write[4] = vd;
fptr[single_offset + 0] = 0.0;
fptr[single_offset + 1] = 0.0;
fptr[single_offset + 2] = 0.0;
fptr[single_offset + 3] = 0.0;
single_offset += 4;
}
//check that everything is as it should be
ERR_FAIL_COND_V(single_offset != (base_offset - stride), 0);
ERR_FAIL_COND_V(((base_offset - stride) + stride * vertex_count) != buffer_size, 0);
}
RD::VertexFormatID vertex_id = RD::get_singleton()->vertex_format_create(descriptions);
ERR_FAIL_COND_V(vertex_id == RD::INVALID_ID, 0);
PolygonBuffers pb;
pb.vertex_buffer = RD::get_singleton()->vertex_buffer_create(polygon_buffer.size(), polygon_buffer);
Vector<RID> buffers;
buffers.resize(descriptions.size());
for (int i = 0; i < descriptions.size(); i++) {
buffers.write[i] = pb.vertex_buffer;
}
pb.vertex_array = RD::get_singleton()->vertex_array_create(p_points.size(), vertex_id, buffers);
if (p_indices.size()) {
//create indices, as indices were requested
PoolVector<uint8_t> index_buffer;
index_buffer.resize(p_indices.size() * sizeof(int32_t));
{
PoolVector<uint8_t>::Write w = index_buffer.write();
copymem(w.ptr(), p_indices.ptr(), sizeof(int32_t) * p_indices.size());
}
pb.index_buffer = RD::get_singleton()->index_buffer_create(p_indices.size(), RD::INDEX_BUFFER_FORMAT_UINT32, index_buffer);
pb.indices = RD::get_singleton()->index_array_create(pb.index_buffer, 0, p_indices.size());
}
pb.vertex_format_id = vertex_id;
PolygonID id = polygon_buffers.last_id++;
polygon_buffers.polygons[id] = pb;
return id;
}
void RasterizerCanvasRD::free_polygon(PolygonID p_polygon) {
PolygonBuffers *pb_ptr = polygon_buffers.polygons.getptr(p_polygon);
ERR_FAIL_COND(!pb_ptr);
PolygonBuffers &pb = *pb_ptr;
if (pb.indices.is_valid()) {
RD::get_singleton()->free(pb.indices);
}
if (pb.index_buffer.is_valid()) {
RD::get_singleton()->free(pb.index_buffer);
}
RD::get_singleton()->free(pb.vertex_array);
RD::get_singleton()->free(pb.vertex_buffer);
polygon_buffers.polygons.erase(p_polygon);
}
Size2i RasterizerCanvasRD::_bind_texture_binding(TextureBindingID p_binding, RD::DrawListID p_draw_list, uint32_t &flags) {
TextureBinding **texture_binding_ptr = bindings.texture_bindings.getptr(p_binding);
ERR_FAIL_COND_V(!texture_binding_ptr, Size2i());
TextureBinding *texture_binding = *texture_binding_ptr;
if (texture_binding->key.normalmap.is_valid()) {
flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
}
if (texture_binding->key.specular.is_valid()) {
flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
if (!RD::get_singleton()->uniform_set_is_valid(texture_binding->uniform_set)) {
//texture may have changed (erased or replaced, see if we can fix)
texture_binding->uniform_set = _create_texture_binding(texture_binding->key.texture, texture_binding->key.normalmap, texture_binding->key.specular, texture_binding->key.texture_filter, texture_binding->key.texture_repeat, texture_binding->key.multimesh);
ERR_FAIL_COND_V(!texture_binding->uniform_set.is_valid(), Size2i(1, 1));
}
RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, texture_binding->uniform_set, 0);
if (texture_binding->key.texture.is_valid()) {
return storage->texture_2d_get_size(texture_binding->key.texture);
} else {
return Size2i(1, 1);
}
}
////////////////////
void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_item, RenderTargetFormat p_render_target_format, RD::TextureSamples p_samples, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights) {
//create an empty push constant
PushConstant push_constant;
Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
_update_transform_2d_to_mat2x3(base_transform, push_constant.world);
Color base_color = p_item->final_modulate;
for (int i = 0; i < 4; i++) {
push_constant.modulation[i] = 0;
push_constant.ninepatch_margins[i] = 0;
push_constant.src_rect[i] = 0;
push_constant.dst_rect[i] = 0;
}
push_constant.flags = 0;
push_constant.color_texture_pixel_size[0] = 0;
push_constant.color_texture_pixel_size[1] = 0;
2019-06-27 02:11:52 +08:00
push_constant.pad[1] = 0;
push_constant.pad[2] = 0;
2019-06-27 02:11:52 +08:00
push_constant.pad[3] = 0;
push_constant.lights[0] = 0;
push_constant.lights[1] = 0;
push_constant.lights[2] = 0;
push_constant.lights[3] = 0;
uint32_t base_flags = 0;
{
Light *light = p_lights;
uint16_t light_count = 0;
while (light) {
if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) {
uint32_t light_index = light->render_index_cache;
push_constant.lights[light_count >> 2] |= light_index << ((light_count & 3) * 8);
light_count++;
if (light->mode == VS::CANVAS_LIGHT_MODE_MASK) {
base_flags |= FLAGS_USING_LIGHT_MASK;
}
if (light_count == MAX_LIGHTS_PER_ITEM) {
break;
}
}
light = light->next_ptr;
}
base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT;
}
PipelineVariants *pipeline_variants = &shader.pipeline_variants;
bool reclip = false;
const Item::Command *c = p_item->commands;
while (c) {
push_constant.flags = base_flags; //reset on each command for sanity
push_constant.specular_shininess = 0xFFFFFFFF;
switch (c->type) {
case Item::Command::TYPE_RECT: {
const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c);
//bind pipeline
{
RID pipeline = pipeline_variants->variants[p_render_target_format][PIPELINE_VARIANT_QUAD].get_render_pipeline(RD::INVALID_ID, p_samples);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
}
//bind textures
Size2 texpixel_size;
{
texpixel_size = _bind_texture_binding(rect->texture_binding.binding_id, p_draw_list, push_constant.flags);
texpixel_size.x = 1.0 / texpixel_size.x;
texpixel_size.y = 1.0 / texpixel_size.y;
}
if (rect->specular_shininess.a < 0.999) {
push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
_update_specular_shininess(rect->specular_shininess, &push_constant.specular_shininess);
Rect2 src_rect;
Rect2 dst_rect;
if (texpixel_size != Vector2()) {
push_constant.color_texture_pixel_size[0] = texpixel_size.x;
push_constant.color_texture_pixel_size[1] = texpixel_size.y;
src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1);
dst_rect = Rect2(rect->rect.position, rect->rect.size);
if (dst_rect.size.width < 0) {
dst_rect.position.x += dst_rect.size.width;
dst_rect.size.width *= -1;
}
if (dst_rect.size.height < 0) {
dst_rect.position.y += dst_rect.size.height;
dst_rect.size.height *= -1;
}
if (rect->flags & CANVAS_RECT_FLIP_H) {
src_rect.size.x *= -1;
}
if (rect->flags & CANVAS_RECT_FLIP_V) {
src_rect.size.y *= -1;
}
if (rect->flags & CANVAS_RECT_TRANSPOSE) {
dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform
}
if (rect->flags & CANVAS_RECT_CLIP_UV) {
push_constant.flags |= FLAGS_CLIP_RECT_UV;
}
} else {
dst_rect = Rect2(rect->rect.position, rect->rect.size);
if (dst_rect.size.width < 0) {
dst_rect.position.x += dst_rect.size.width;
dst_rect.size.width *= -1;
}
if (dst_rect.size.height < 0) {
dst_rect.position.y += dst_rect.size.height;
dst_rect.size.height *= -1;
}
src_rect = Rect2(0, 0, 1, 1);
texpixel_size = Vector2(1, 1);
}
push_constant.modulation[0] = rect->modulate.r * base_color.r;
push_constant.modulation[1] = rect->modulate.g * base_color.g;
push_constant.modulation[2] = rect->modulate.b * base_color.b;
push_constant.modulation[3] = rect->modulate.a * base_color.a;
push_constant.src_rect[0] = src_rect.position.x;
push_constant.src_rect[1] = src_rect.position.y;
push_constant.src_rect[2] = src_rect.size.width;
push_constant.src_rect[3] = src_rect.size.height;
push_constant.dst_rect[0] = dst_rect.position.x;
push_constant.dst_rect[1] = dst_rect.position.y;
push_constant.dst_rect[2] = dst_rect.size.width;
push_constant.dst_rect[3] = dst_rect.size.height;
push_constant.color_texture_pixel_size[0] = texpixel_size.x;
push_constant.color_texture_pixel_size[1] = texpixel_size.y;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array);
RD::get_singleton()->draw_list_draw(p_draw_list, true);
} break;
case Item::Command::TYPE_NINEPATCH: {
const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c);
//bind pipeline
{
RID pipeline = pipeline_variants->variants[p_render_target_format][PIPELINE_VARIANT_NINEPATCH].get_render_pipeline(RD::INVALID_ID, p_samples);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
}
//bind textures
Size2 texpixel_size;
{
texpixel_size = _bind_texture_binding(np->texture_binding.binding_id, p_draw_list, push_constant.flags);
texpixel_size.x = 1.0 / texpixel_size.x;
texpixel_size.y = 1.0 / texpixel_size.y;
}
if (np->specular_shininess.a < 0.999) {
push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
_update_specular_shininess(np->specular_shininess, &push_constant.specular_shininess);
Rect2 src_rect;
Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y);
if (texpixel_size == Size2()) {
texpixel_size = Size2(1, 1);
src_rect = Rect2(0, 0, 1, 1);
} else {
if (np->source != Rect2()) {
src_rect = Rect2(np->source.position.x * texpixel_size.width, np->source.position.y * texpixel_size.height, np->source.size.x * texpixel_size.width, np->source.size.y * texpixel_size.height);
texpixel_size = Size2(1.0 / np->source.size.width, 1.0 / np->source.size.height);
} else {
src_rect = Rect2(0, 0, 1, 1);
}
}
push_constant.modulation[0] = np->color.r * base_color.r;
push_constant.modulation[1] = np->color.g * base_color.g;
push_constant.modulation[2] = np->color.b * base_color.b;
push_constant.modulation[3] = np->color.a * base_color.a;
push_constant.src_rect[0] = src_rect.position.x;
push_constant.src_rect[1] = src_rect.position.y;
push_constant.src_rect[2] = src_rect.size.width;
push_constant.src_rect[3] = src_rect.size.height;
push_constant.dst_rect[0] = dst_rect.position.x;
push_constant.dst_rect[1] = dst_rect.position.y;
push_constant.dst_rect[2] = dst_rect.size.width;
push_constant.dst_rect[3] = dst_rect.size.height;
push_constant.color_texture_pixel_size[0] = texpixel_size.x;
push_constant.color_texture_pixel_size[1] = texpixel_size.y;
push_constant.flags |= int(np->axis_x) << FLAGS_NINEPATCH_H_MODE_SHIFT;
push_constant.flags |= int(np->axis_y) << FLAGS_NINEPATCH_V_MODE_SHIFT;
if (np->draw_center) {
push_constant.flags |= FLAGS_NINEPACH_DRAW_CENTER;
}
push_constant.ninepatch_margins[0] = np->margin[MARGIN_LEFT];
push_constant.ninepatch_margins[1] = np->margin[MARGIN_TOP];
push_constant.ninepatch_margins[2] = np->margin[MARGIN_RIGHT];
push_constant.ninepatch_margins[3] = np->margin[MARGIN_BOTTOM];
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array);
RD::get_singleton()->draw_list_draw(p_draw_list, true);
} break;
case Item::Command::TYPE_POLYGON: {
const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c);
PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id);
ERR_CONTINUE(!pb);
//bind pipeline
{
static const PipelineVariant variant[VS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES };
ERR_CONTINUE(polygon->primitive < 0 || polygon->primitive >= VS::PRIMITIVE_MAX);
RID pipeline = pipeline_variants->variants[p_render_target_format][variant[polygon->primitive]].get_render_pipeline(pb->vertex_format_id, p_samples);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
}
if (polygon->primitive == VS::PRIMITIVE_LINES) {
//not supported in most hardware, so pointless
//RD::get_singleton()->draw_list_set_line_width(p_draw_list, polygon->line_width);
}
//bind textures
Size2 texpixel_size;
{
texpixel_size = _bind_texture_binding(polygon->texture_binding.binding_id, p_draw_list, push_constant.flags);
texpixel_size.x = 1.0 / texpixel_size.x;
texpixel_size.y = 1.0 / texpixel_size.y;
}
if (polygon->specular_shininess.a < 0.999) {
push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
_update_specular_shininess(polygon->specular_shininess, &push_constant.specular_shininess);
push_constant.modulation[0] = base_color.r;
push_constant.modulation[1] = base_color.g;
push_constant.modulation[2] = base_color.b;
push_constant.modulation[3] = base_color.a;
for (int j = 0; j < 4; j++) {
push_constant.src_rect[j] = 0;
push_constant.dst_rect[j] = 0;
push_constant.ninepatch_margins[j] = 0;
}
push_constant.color_texture_pixel_size[0] = texpixel_size.x;
push_constant.color_texture_pixel_size[1] = texpixel_size.y;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, pb->vertex_array);
if (pb->indices.is_valid()) {
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, pb->indices);
}
RD::get_singleton()->draw_list_draw(p_draw_list, pb->indices.is_valid());
} break;
case Item::Command::TYPE_PRIMITIVE: {
const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c);
//bind pipeline
{
static const PipelineVariant variant[4] = { PIPELINE_VARIANT_PRIMITIVE_POINTS, PIPELINE_VARIANT_PRIMITIVE_LINES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES };
ERR_CONTINUE(primitive->point_count == 0 || primitive->point_count > 4);
RID pipeline = pipeline_variants->variants[p_render_target_format][variant[primitive->point_count - 1]].get_render_pipeline(RD::INVALID_ID, p_samples);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
}
//bind textures
{
_bind_texture_binding(primitive->texture_binding.binding_id, p_draw_list, push_constant.flags);
}
if (primitive->specular_shininess.a < 0.999) {
push_constant.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
_update_specular_shininess(primitive->specular_shininess, &push_constant.specular_shininess);
2019-06-27 02:11:52 +08:00
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3, primitive->point_count) - 1]);
for (uint32_t j = 0; j < MIN(3, primitive->point_count); j++) {
push_constant.points[j * 2 + 0] = primitive->points[j].x;
push_constant.points[j * 2 + 1] = primitive->points[j].y;
push_constant.uvs[j * 2 + 0] = primitive->uvs[j].x;
push_constant.uvs[j * 2 + 1] = primitive->uvs[j].y;
Color col = primitive->colors[j] * base_color;
push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
2019-06-27 02:11:52 +08:00
RD::get_singleton()->draw_list_draw(p_draw_list, true);
2019-06-27 02:11:52 +08:00
if (primitive->point_count == 4) {
for (uint32_t j = 1; j < 3; j++) {
//second half of triangle
push_constant.points[j * 2 + 0] = primitive->points[j + 1].x;
push_constant.points[j * 2 + 1] = primitive->points[j + 1].y;
push_constant.uvs[j * 2 + 0] = primitive->uvs[j + 1].x;
push_constant.uvs[j * 2 + 1] = primitive->uvs[j + 1].y;
Color col = primitive->colors[j + 1] * base_color;
2019-06-27 02:11:52 +08:00
push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
2019-06-27 02:11:52 +08:00
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_draw(p_draw_list, true);
}
} break;
#if 0
case Item::Command::TYPE_MESH: {
Item::CommandMesh *mesh = static_cast<Item::CommandMesh *>(c);
_set_texture_rect_mode(false);
RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mesh->texture, mesh->normal_map);
if (texture) {
Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
}
state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * mesh->transform);
RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.getornull(mesh->mesh);
if (mesh_data) {
for (int j = 0; j < mesh_data->surfaces.size(); j++) {
RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j];
// materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing
glBindVertexArray(s->array_id);
glVertexAttrib4f(VS::ARRAY_COLOR, mesh->modulate.r, mesh->modulate.g, mesh->modulate.b, mesh->modulate.a);
if (s->index_array_len) {
glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0);
} else {
glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
}
glBindVertexArray(0);
}
}
state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform);
} break;
case Item::Command::TYPE_MULTIMESH: {
Item::CommandMultiMesh *mmesh = static_cast<Item::CommandMultiMesh *>(c);
RasterizerStorageGLES3::MultiMesh *multi_mesh = storage->multimesh_owner.getornull(mmesh->multimesh);
if (!multi_mesh)
break;
RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.getornull(multi_mesh->mesh);
if (!mesh_data)
break;
RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mmesh->texture, mmesh->normal_map);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, multi_mesh->custom_data_format != VS::MULTIMESH_CUSTOM_DATA_NONE);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true);
//reset shader and force rebind
state.using_texture_rect = true;
_set_texture_rect_mode(false);
if (texture) {
Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
}
int amount = MIN(multi_mesh->size, multi_mesh->visible_instances);
if (amount == -1) {
amount = multi_mesh->size;
}
for (int j = 0; j < mesh_data->surfaces.size(); j++) {
RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j];
// materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing
glBindVertexArray(s->instancing_array_id);
glBindBuffer(GL_ARRAY_BUFFER, multi_mesh->buffer); //modify the buffer
int stride = (multi_mesh->xform_floats + multi_mesh->color_floats + multi_mesh->custom_data_floats) * 4;
glEnableVertexAttribArray(8);
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0));
glVertexAttribDivisor(8, 1);
glEnableVertexAttribArray(9);
glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(4 * 4));
glVertexAttribDivisor(9, 1);
int color_ofs;
if (multi_mesh->transform_format == VS::MULTIMESH_TRANSFORM_3D) {
glEnableVertexAttribArray(10);
glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(8 * 4));
glVertexAttribDivisor(10, 1);
color_ofs = 12 * 4;
} else {
glDisableVertexAttribArray(10);
glVertexAttrib4f(10, 0, 0, 1, 0);
color_ofs = 8 * 4;
}
int custom_data_ofs = color_ofs;
switch (multi_mesh->color_format) {
case VS::MULTIMESH_COLOR_NONE: {
glDisableVertexAttribArray(11);
glVertexAttrib4f(11, 1, 1, 1, 1);
} break;
case VS::MULTIMESH_COLOR_8BIT: {
glEnableVertexAttribArray(11);
glVertexAttribPointer(11, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(color_ofs));
glVertexAttribDivisor(11, 1);
custom_data_ofs += 4;
} break;
case VS::MULTIMESH_COLOR_FLOAT: {
glEnableVertexAttribArray(11);
glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(color_ofs));
glVertexAttribDivisor(11, 1);
custom_data_ofs += 4 * 4;
} break;
}
switch (multi_mesh->custom_data_format) {
case VS::MULTIMESH_CUSTOM_DATA_NONE: {
glDisableVertexAttribArray(12);
glVertexAttrib4f(12, 1, 1, 1, 1);
} break;
case VS::MULTIMESH_CUSTOM_DATA_8BIT: {
glEnableVertexAttribArray(12);
glVertexAttribPointer(12, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(custom_data_ofs));
glVertexAttribDivisor(12, 1);
} break;
case VS::MULTIMESH_CUSTOM_DATA_FLOAT: {
glEnableVertexAttribArray(12);
glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(custom_data_ofs));
glVertexAttribDivisor(12, 1);
} break;
}
if (s->index_array_len) {
glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount);
} else {
glDrawArraysInstanced(gl_primitive[s->primitive], 0, s->array_len, amount);
}
glBindVertexArray(0);
}
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
state.using_texture_rect = true;
_set_texture_rect_mode(false);
} break;
case Item::Command::TYPE_PARTICLES: {
Item::CommandParticles *particles_cmd = static_cast<Item::CommandParticles *>(c);
RasterizerStorageGLES3::Particles *particles = storage->particles_owner.getornull(particles_cmd->particles);
if (!particles)
break;
if (particles->inactive && !particles->emitting)
break;
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); //not used, so keep white
VisualServerRaster::redraw_request();
storage->particles_request_process(particles_cmd->particles);
//enable instancing
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, true);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, true);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true);
//reset shader and force rebind
state.using_texture_rect = true;
_set_texture_rect_mode(false);
RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(particles_cmd->texture, particles_cmd->normal_map);
if (texture) {
Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
} else {
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, Vector2(1.0, 1.0));
}
if (!particles->use_local_coords) {
Transform2D inv_xf;
inv_xf.set_axis(0, Vector2(particles->emission_transform.basis.get_axis(0).x, particles->emission_transform.basis.get_axis(0).y));
inv_xf.set_axis(1, Vector2(particles->emission_transform.basis.get_axis(1).x, particles->emission_transform.basis.get_axis(1).y));
inv_xf.set_origin(Vector2(particles->emission_transform.get_origin().x, particles->emission_transform.get_origin().y));
inv_xf.affine_invert();
state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * inv_xf);
}
glBindVertexArray(data.particle_quad_array); //use particle quad array
glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //bind particle buffer
int stride = sizeof(float) * 4 * 6;
int amount = particles->amount;
if (particles->draw_order != VS::PARTICLES_DRAW_ORDER_LIFETIME) {
glEnableVertexAttribArray(8); //xform x
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 3));
glVertexAttribDivisor(8, 1);
glEnableVertexAttribArray(9); //xform y
glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 4));
glVertexAttribDivisor(9, 1);
glEnableVertexAttribArray(10); //xform z
glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 5));
glVertexAttribDivisor(10, 1);
glEnableVertexAttribArray(11); //color
glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, NULL);
glVertexAttribDivisor(11, 1);
glEnableVertexAttribArray(12); //custom
glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 2));
glVertexAttribDivisor(12, 1);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount);
} else {
//split
int split = int(Math::ceil(particles->phase * particles->amount));
if (amount - split > 0) {
glEnableVertexAttribArray(8); //xform x
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 3));
glVertexAttribDivisor(8, 1);
glEnableVertexAttribArray(9); //xform y
glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 4));
glVertexAttribDivisor(9, 1);
glEnableVertexAttribArray(10); //xform z
glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 5));
glVertexAttribDivisor(10, 1);
glEnableVertexAttribArray(11); //color
glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + 0));
glVertexAttribDivisor(11, 1);
glEnableVertexAttribArray(12); //custom
glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * split + sizeof(float) * 4 * 2));
glVertexAttribDivisor(12, 1);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount - split);
}
if (split > 0) {
glEnableVertexAttribArray(8); //xform x
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 3));
glVertexAttribDivisor(8, 1);
glEnableVertexAttribArray(9); //xform y
glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 4));
glVertexAttribDivisor(9, 1);
glEnableVertexAttribArray(10); //xform z
glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 5));
glVertexAttribDivisor(10, 1);
glEnableVertexAttribArray(11); //color
glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, NULL);
glVertexAttribDivisor(11, 1);
glEnableVertexAttribArray(12); //custom
glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * 2));
glVertexAttribDivisor(12, 1);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, split);
}
}
glBindVertexArray(0);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
state.using_texture_rect = true;
_set_texture_rect_mode(false);
} break;
#endif
case Item::Command::TYPE_TRANSFORM: {
const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
_update_transform_2d_to_mat2x3(base_transform * transform->xform, push_constant.world);
} break;
case Item::Command::TYPE_CLIP_IGNORE: {
const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c);
if (current_clip) {
if (ci->ignore != reclip) {
if (ci->ignore) {
RD::get_singleton()->draw_list_disable_scissor(p_draw_list);
reclip = true;
} else {
RD::get_singleton()->draw_list_enable_scissor(p_draw_list, current_clip->final_clip_rect);
reclip = false;
}
}
}
} break;
}
c = c->next;
}
if (current_clip && reclip) {
//will make it re-enable clipping if needed afterwards
current_clip = NULL;
}
}
void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights) {
Item *current_clip = NULL;
RenderTargetFormat render_target_format = RENDER_TARGET_FORMAT_8_BIT_INT;
Transform2D canvas_transform_inverse = p_canvas_transform_inverse;
RID framebuffer = storage->render_target_get_rd_framebuffer(p_to_render_target);
Vector<Color> clear_colors;
bool clear = false;
if (storage->render_target_is_clear_requested(p_to_render_target)) {
clear = true;
clear_colors.push_back(storage->render_target_get_clear_request_color(p_to_render_target));
storage->render_target_disable_clear_request(p_to_render_target);
}
#warning TODO obtain from framebuffer format eventually when this is implemented
RD::TextureSamples texture_samples = RD::TEXTURE_SAMPLES_1;
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_KEEP_COLOR, RD::FINAL_ACTION_READ_COLOR_DISCARD_DEPTH, clear_colors);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.canvas_state_uniform_set, 3);
for (int i = 0; i < p_item_count; i++) {
Item *ci = items[i];
if (current_clip != ci->final_clip_owner) {
current_clip = ci->final_clip_owner;
//setup clip
if (current_clip) {
RD::get_singleton()->draw_list_enable_scissor(draw_list, current_clip->final_clip_rect);
} else {
RD::get_singleton()->draw_list_disable_scissor(draw_list);
}
}
if (false) { //not skeleton
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, shader.default_skeleton_uniform_set, 1);
}
_render_item(draw_list, ci, render_target_format, texture_samples, canvas_transform_inverse, current_clip, p_lights);
}
RD::get_singleton()->draw_list_end();
}
void RasterizerCanvasRD::_update_canvas_state_uniform_set() {
if (state.canvas_state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state.canvas_state_uniform_set)) {
return; //nothing to update
}
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
u.binding = 0;
u.ids.push_back(state.canvas_state_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
u.binding = 1;
u.ids.push_back(state.lights_uniform_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u_lights;
u_lights.type = RD::UNIFORM_TYPE_TEXTURE;
u_lights.binding = 2;
RD::Uniform u_shadows;
u_shadows.type = RD::UNIFORM_TYPE_TEXTURE;
u_shadows.binding = 3;
//lights
for (uint32_t i = 0; i < MAX_LIGHT_TEXTURES; i++) {
if (i < canvas_light_owner.get_rid_count()) {
CanvasLight *cl = canvas_light_owner.get_rid_by_index(i);
cl->texture_index = i;
RID rd_texture;
if (cl->texture.is_valid()) {
rd_texture = storage->texture_get_rd_texture(cl->texture);
}
if (rd_texture.is_valid()) {
u_lights.ids.push_back(rd_texture);
} else {
u_lights.ids.push_back(default_textures.white_texture);
}
if (cl->shadow.texture.is_valid()) {
u_shadows.ids.push_back(cl->shadow.texture);
} else {
u_shadows.ids.push_back(default_textures.black_texture);
}
} else {
u_lights.ids.push_back(default_textures.white_texture);
u_shadows.ids.push_back(default_textures.black_texture);
}
}
//in case there are more
for (uint32_t i = MAX_LIGHT_TEXTURES; i < canvas_light_owner.get_rid_count(); i++) {
CanvasLight *cl = canvas_light_owner.get_rid_by_index(i);
cl->texture_index = -1; //make invalid (no texture)
}
uniforms.push_back(u_lights);
uniforms.push_back(u_shadows);
}
state.canvas_state_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, 3); // uses index 3
}
void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform) {
int item_count = 0;
//setup canvas state uniforms if needed
_update_canvas_state_uniform_set();
Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse();
{
//update canvas state uniform buffer
State::Buffer state_buffer;
Size2i ssize = storage->render_target_get_size(p_to_render_target);
Transform screen_transform;
screen_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f);
screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f));
_update_transform_to_mat4(screen_transform, state_buffer.screen_transform);
_update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform);
Transform2D normal_transform = p_canvas_transform;
normal_transform.elements[0].normalize();
normal_transform.elements[1].normalize();
normal_transform.elements[2] = Vector2();
_update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform);
state_buffer.canvas_modulate[0] = p_modulate.r;
state_buffer.canvas_modulate[1] = p_modulate.g;
state_buffer.canvas_modulate[2] = p_modulate.b;
state_buffer.canvas_modulate[3] = p_modulate.a;
RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer, true);
}
//setup lights if exist
{
Light *l = p_light_list;
uint32_t index = 0;
while (l) {
if (index == MAX_RENDER_LIGHTS) {
l->render_index_cache = -1;
l = l->next_ptr;
continue;
}
CanvasLight *clight = canvas_light_owner.getornull(l->light_internal);
if (!clight || clight->texture_index < 0) { //unused or invalid texture
l->render_index_cache = -1;
l = l->next_ptr;
ERR_CONTINUE(!clight);
}
Transform2D to_light_xform = (p_canvas_transform * l->light_shader_xform).affine_inverse();
Vector2 canvas_light_pos = p_canvas_transform.xform(l->xform.get_origin()); //convert light position to canvas coordinates, as all computation is done in canvas coords to avoid precision loss
state.light_uniforms[index].position[0] = canvas_light_pos.x;
state.light_uniforms[index].position[1] = canvas_light_pos.y;
_update_transform_2d_to_mat2x4(to_light_xform, state.light_uniforms[index].matrix);
state.light_uniforms[index].height = l->height * (p_canvas_transform.elements[0].length() + p_canvas_transform.elements[1].length()) * 0.5; //approximate height conversion to the canvas size, since all calculations are done in canvas coords to avoid precision loss
for (int i = 0; i < 4; i++) {
state.light_uniforms[index].shadow_color[i] = l->shadow_color[i];
state.light_uniforms[index].color[i] = l->color[i];
}
state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate
if (clight->shadow.texture.is_valid()) {
state.light_uniforms[index].shadow_pixel_size = 1.0 / clight->shadow.size;
} else {
state.light_uniforms[index].shadow_pixel_size = 1.0;
}
state.light_uniforms[index].flags = clight->texture_index;
state.light_uniforms[index].flags |= l->mode << LIGHT_FLAGS_BLEND_SHIFT;
l->render_index_cache = index;
index++;
l = l->next_ptr;
}
if (index > 0) {
RD::get_singleton()->buffer_update(state.lights_uniform_buffer, 0, sizeof(LightUniform) * index, &state.light_uniforms[0], true);
}
}
//fill the list until rendering is possible.
Item *ci = p_item_list;
while (ci) {
items[item_count++] = ci;
bool backbuffer_copy = ci->copy_back_buffer; // || shader uses SCREEN_TEXTURE
if (!ci->next || backbuffer_copy || item_count == MAX_RENDER_ITEMS - 1) {
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
//then reset
item_count = 0;
}
if (ci->copy_back_buffer) {
if (ci->copy_back_buffer->full) {
//_copy_texscreen(Rect2());
} else {
//_copy_texscreen(ci->copy_back_buffer->rect);
}
}
ci = ci->next;
}
}
RID RasterizerCanvasRD::light_create() {
CanvasLight canvas_light;
canvas_light.shadow.size = 0;
canvas_light.texture_index = -1;
return canvas_light_owner.make_rid(canvas_light);
}
void RasterizerCanvasRD::light_set_texture(RID p_rid, RID p_texture) {
CanvasLight *cl = canvas_light_owner.getornull(p_rid);
ERR_FAIL_COND(!cl);
if (cl->texture == p_texture) {
return;
}
cl->texture = p_texture;
//canvas state uniform set needs updating
if (state.canvas_state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state.canvas_state_uniform_set)) {
RD::get_singleton()->free(state.canvas_state_uniform_set);
}
}
void RasterizerCanvasRD::light_set_use_shadow(RID p_rid, bool p_enable, int p_resolution) {
CanvasLight *cl = canvas_light_owner.getornull(p_rid);
ERR_FAIL_COND(!cl);
ERR_FAIL_COND(p_resolution < 64);
if (cl->shadow.texture.is_valid() == p_enable && p_resolution == cl->shadow.size) {
return;
}
if (cl->shadow.texture.is_valid()) {
RD::get_singleton()->free(cl->shadow.uniform_set);
cl->shadow.uniform_set = RID();
for (int i = 0; i < 4; i++) {
RD::get_singleton()->free(cl->shadow.render_fb[i]);
RD::get_singleton()->free(cl->shadow.render_textures[i]);
cl->shadow.render_fb[i] = RID();
cl->shadow.render_textures[i] = RID();
}
RD::get_singleton()->free(cl->shadow.fix_fb);
RD::get_singleton()->free(cl->shadow.texture);
cl->shadow.fix_fb = RID();
cl->shadow.texture = RID();
}
if (p_enable) {
{ //texture
RD::TextureFormat tf;
tf.type = RD::TEXTURE_TYPE_2D;
tf.width = p_resolution;
tf.height = 1;
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
tf.format = RD::DATA_FORMAT_R32_SFLOAT;
cl->shadow.texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
Vector<RID> fb_textures;
fb_textures.push_back(cl->shadow.texture);
cl->shadow.fix_fb = RD::get_singleton()->framebuffer_create(fb_textures);
}
{
RD::TextureFormat tf;
tf.type = RD::TEXTURE_TYPE_2D;
tf.width = p_resolution / 2;
tf.height = 1;
tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_X8_D24_UNORM_PACK32, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_X8_D24_UNORM_PACK32 : RD::DATA_FORMAT_D32_SFLOAT;
//chunks to write
cl->shadow.render_depth = RD::get_singleton()->texture_create(tf, RD::TextureView());
RD::Uniform tex_uniforms;
tex_uniforms.type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
tex_uniforms.binding = 0;
for (int i = 0; i < 4; i++) {
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
tf.format = RD::DATA_FORMAT_R32_SFLOAT;
cl->shadow.render_textures[i] = RD::get_singleton()->texture_create(tf, RD::TextureView());
Vector<RID> textures;
textures.push_back(cl->shadow.render_textures[i]);
textures.push_back(cl->shadow.render_depth);
cl->shadow.render_fb[i] = RD::get_singleton()->framebuffer_create(textures);
tex_uniforms.ids.push_back(default_samplers.samplers[VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST][VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED]);
tex_uniforms.ids.push_back(cl->shadow.render_textures[i]);
}
Vector<RD::Uniform> tex_uniforms_set;
tex_uniforms_set.push_back(tex_uniforms);
cl->shadow.uniform_set = RD::get_singleton()->uniform_set_create(tex_uniforms_set, shadow_render.shader_fix.version_get_shader(shadow_render.shader_fix_version, 0), 0);
}
}
//canvas state uniform set needs updating
if (state.canvas_state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state.canvas_state_uniform_set)) {
RD::get_singleton()->free(state.canvas_state_uniform_set);
}
}
void RasterizerCanvasRD::light_update_shadow(RID p_rid, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) {
CanvasLight *cl = canvas_light_owner.getornull(p_rid);
ERR_FAIL_COND(cl->shadow.texture.is_null());
for (int i = 0; i < 4; i++) {
//make sure it remains orthogonal, makes easy to read angle later
//light.basis.scale(Vector3(to_light.elements[0].length(),to_light.elements[1].length(),1));
Vector<Color> cc;
cc.push_back(Color(p_far, p_far, p_far, 1.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(cl->shadow.render_fb[i], RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ_COLOR_DISCARD_DEPTH, cc);
CameraMatrix projection;
{
real_t fov = 90;
real_t nearp = p_near;
real_t farp = p_far;
real_t aspect = 1.0;
real_t ymax = nearp * Math::tan(Math::deg2rad(fov * 0.5));
real_t ymin = -ymax;
real_t xmin = ymin * aspect;
real_t xmax = ymax * aspect;
projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
}
Vector3 cam_target = Basis(Vector3(0, 0, Math_PI * 2 * (i / 4.0))).xform(Vector3(0, 1, 0));
projection = projection * CameraMatrix(Transform().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse());
ShadowRenderPushConstant push_constant;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
push_constant.projection[y * 4 + x] = projection.matrix[y][x];
}
}
/*if (i == 0)
*p_xform_cache = projection;*/
LightOccluderInstance *instance = p_occluders;
while (instance) {
OccluderPolygon *co = occluder_polygon_owner.getornull(instance->polygon);
if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) {
instance = instance->next;
continue;
}
_update_transform_2d_to_mat4(p_light_xform * instance->xform_cache, push_constant.modelview);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]);
RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array);
RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array);
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
instance = instance->next;
}
RD::get_singleton()->draw_list_end();
}
Vector<Color> cc;
cc.push_back(Color(p_far, p_far, p_far, 1.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(cl->shadow.fix_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ_COLOR_DISCARD_DEPTH, cc);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.shader_fix_pipeline);
RD::get_singleton()->draw_list_bind_index_array(draw_list, primitive_arrays.index_array[3]);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, cl->shadow.uniform_set, 0);
RD::get_singleton()->draw_list_draw(draw_list, true);
RD::get_singleton()->draw_list_end();
}
RID RasterizerCanvasRD::occluder_polygon_create() {
OccluderPolygon occluder;
occluder.point_count = 0;
occluder.cull_mode = VS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED;
return occluder_polygon_owner.make_rid(occluder);
}
void RasterizerCanvasRD::occluder_polygon_set_shape_as_lines(RID p_occluder, const PoolVector<Vector2> &p_lines) {
OccluderPolygon *oc = occluder_polygon_owner.getornull(p_occluder);
ERR_FAIL_COND(!oc);
if (oc->point_count != p_lines.size() && oc->vertex_array.is_valid()) {
RD::get_singleton()->free(oc->vertex_array);
RD::get_singleton()->free(oc->vertex_buffer);
RD::get_singleton()->free(oc->index_array);
RD::get_singleton()->free(oc->index_buffer);
oc->vertex_array = RID();
oc->vertex_buffer = RID();
oc->index_array = RID();
oc->index_buffer = RID();
}
if (p_lines.size()) {
PoolVector<uint8_t> geometry;
PoolVector<uint8_t> indices;
int lc = p_lines.size();
geometry.resize(lc * 6 * sizeof(float));
indices.resize(lc * 3 * sizeof(uint16_t));
{
PoolVector<uint8_t>::Write vw = geometry.write();
float *vwptr = (float *)vw.ptr();
PoolVector<uint8_t>::Write iw = indices.write();
uint16_t *iwptr = (uint16_t *)iw.ptr();
PoolVector<Vector2>::Read lr = p_lines.read();
const int POLY_HEIGHT = 16384;
for (int i = 0; i < lc / 2; i++) {
vwptr[i * 12 + 0] = lr[i * 2 + 0].x;
vwptr[i * 12 + 1] = lr[i * 2 + 0].y;
vwptr[i * 12 + 2] = POLY_HEIGHT;
vwptr[i * 12 + 3] = lr[i * 2 + 1].x;
vwptr[i * 12 + 4] = lr[i * 2 + 1].y;
vwptr[i * 12 + 5] = POLY_HEIGHT;
vwptr[i * 12 + 6] = lr[i * 2 + 1].x;
vwptr[i * 12 + 7] = lr[i * 2 + 1].y;
vwptr[i * 12 + 8] = -POLY_HEIGHT;
vwptr[i * 12 + 9] = lr[i * 2 + 0].x;
vwptr[i * 12 + 10] = lr[i * 2 + 0].y;
vwptr[i * 12 + 11] = -POLY_HEIGHT;
iwptr[i * 6 + 0] = i * 4 + 0;
iwptr[i * 6 + 1] = i * 4 + 1;
iwptr[i * 6 + 2] = i * 4 + 2;
iwptr[i * 6 + 3] = i * 4 + 2;
iwptr[i * 6 + 4] = i * 4 + 3;
iwptr[i * 6 + 5] = i * 4 + 0;
}
}
//if same buffer len is being set, just use BufferSubData to avoid a pipeline flush
if (oc->vertex_array.is_null()) {
//create from scratch
//vertices
oc->vertex_buffer = RD::get_singleton()->vertex_buffer_create(lc * 6 * sizeof(real_t), geometry);
Vector<RID> buffer;
buffer.push_back(oc->vertex_buffer);
oc->vertex_array = RD::get_singleton()->vertex_array_create(4 * lc / 2, shadow_render.vertex_format, buffer);
//indices
oc->index_buffer = RD::get_singleton()->index_buffer_create(3 * lc, RD::INDEX_BUFFER_FORMAT_UINT16, indices);
oc->index_array = RD::get_singleton()->index_array_create(oc->index_buffer, 0, 3 * lc);
} else {
//update existing
PoolVector<uint8_t>::Read vr = geometry.read();
RD::get_singleton()->buffer_update(oc->vertex_buffer, 0, geometry.size(), vr.ptr());
PoolVector<uint8_t>::Read ir = indices.read();
RD::get_singleton()->buffer_update(oc->index_buffer, 0, indices.size(), ir.ptr());
}
}
}
void RasterizerCanvasRD::occluder_polygon_set_cull_mode(RID p_occluder, VS::CanvasOccluderPolygonCullMode p_mode) {
OccluderPolygon *oc = occluder_polygon_owner.getornull(p_occluder);
ERR_FAIL_COND(!oc);
oc->cull_mode = p_mode;
}
void RasterizerCanvasRD::update() {
_dispose_bindings();
}
RasterizerCanvasRD::RasterizerCanvasRD(RasterizerStorageRD *p_storage) {
storage = p_storage;
{ //create default textures
RD::TextureFormat tformat;
tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
tformat.width = 4;
tformat.height = 4;
tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
tformat.type = RD::TEXTURE_TYPE_2D;
PoolVector<uint8_t> pv;
pv.resize(16 * 4);
for (int i = 0; i < 16; i++) {
pv.set(i * 4 + 0, 255);
pv.set(i * 4 + 1, 255);
pv.set(i * 4 + 2, 255);
pv.set(i * 4 + 3, 255);
}
{
Vector<PoolVector<uint8_t> > vpv;
vpv.push_back(pv);
default_textures.white_texture = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
}
for (int i = 0; i < 16; i++) {
pv.set(i * 4 + 0, 0);
pv.set(i * 4 + 1, 0);
pv.set(i * 4 + 2, 0);
pv.set(i * 4 + 3, 255);
}
{
Vector<PoolVector<uint8_t> > vpv;
vpv.push_back(pv);
default_textures.black_texture = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
}
for (int i = 0; i < 16; i++) {
pv.set(i * 4 + 0, 128);
pv.set(i * 4 + 1, 128);
pv.set(i * 4 + 2, 255);
pv.set(i * 4 + 3, 255);
}
{
Vector<PoolVector<uint8_t> > vpv;
vpv.push_back(pv);
default_textures.normal_texture = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
}
for (int i = 0; i < 16; i++) {
pv.set(i * 4 + 0, 255);
pv.set(i * 4 + 1, 128);
pv.set(i * 4 + 2, 255);
pv.set(i * 4 + 3, 255);
}
{
Vector<PoolVector<uint8_t> > vpv;
vpv.push_back(pv);
default_textures.aniso_texture = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
}
for (int i = 0; i < 16; i++) {
pv.set(i * 4 + 0, 0);
pv.set(i * 4 + 1, 0);
pv.set(i * 4 + 2, 0);
pv.set(i * 4 + 3, 0);
}
default_textures.default_multimesh_tb = RD::get_singleton()->texture_buffer_create(16, RD::DATA_FORMAT_R8G8B8A8_UNORM, pv);
}
{ //create default samplers
for (int i = 1; i < VS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) {
for (int j = 1; j < VS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) {
RD::SamplerState sampler_state;
switch (i) {
case VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: {
sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST;
sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST;
sampler_state.max_lod = 0;
} break;
case VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR: {
sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR;
sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR;
sampler_state.max_lod = 0;
} break;
case VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: {
sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR;
sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR;
sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR;
} break;
case VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIMPAMPS: {
sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST;
sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR;
sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR;
} break;
default: {
}
}
switch (j) {
case VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED: {
sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE;
sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE;
} break;
case VS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: {
sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_REPEAT;
sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_REPEAT;
} break;
case VS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: {
sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT;
sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT;
} break;
default: {
}
}
default_samplers.samplers[i][j] = RD::get_singleton()->sampler_create(sampler_state);
}
}
default_samplers.default_filter = VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
default_samplers.default_repeat = VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
}
{ //shader variants
Vector<String> variants;
variants.push_back(""); //none by default is first variant
variants.push_back("#define USE_NINEPATCH\n"); //ninepatch is the second variant
variants.push_back("#define USE_PRIMITIVE\n"); //primitve is the third
variants.push_back("#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size
variants.push_back("#define USE_ATTRIBUTES\n"); // attributes for vertex arrays
variants.push_back("#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size
shader.canvas_shader.initialize(variants);
shader.default_version = shader.canvas_shader.version_create();
{
//framebuffer formats
RD::AttachmentFormat af;
af.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
af.samples = RD::TEXTURE_SAMPLES_1;
2019-06-27 02:11:52 +08:00
af.usage_flags = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_RETRIEVE_BIT;
Vector<RD::AttachmentFormat> formats;
formats.push_back(af);
shader.framebuffer_formats[RENDER_TARGET_FORMAT_8_BIT_INT] = RD::get_singleton()->framebuffer_format_create(formats);
formats.clear();
af.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
formats.push_back(af);
shader.framebuffer_formats[RENDER_TARGET_FORMAT_16_BIT_FLOAT] = RD::get_singleton()->framebuffer_format_create(formats);
}
for (int i = 0; i < RENDER_TARGET_FORMAT_MAX; i++) {
RD::FramebufferFormatID fb_format = shader.framebuffer_formats[i];
for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) {
RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = {
RD::RENDER_PRIMITIVE_TRIANGLES,
RD::RENDER_PRIMITIVE_TRIANGLES,
RD::RENDER_PRIMITIVE_TRIANGLES,
RD::RENDER_PRIMITIVE_LINES,
RD::RENDER_PRIMITIVE_POINTS,
RD::RENDER_PRIMITIVE_TRIANGLES,
RD::RENDER_PRIMITIVE_LINES,
RD::RENDER_PRIMITIVE_POINTS,
};
ShaderVariant shader_variants[PIPELINE_VARIANT_MAX] = {
SHADER_VARIANT_QUAD,
SHADER_VARIANT_NINEPATCH,
SHADER_VARIANT_PRIMITIVE,
SHADER_VARIANT_PRIMITIVE,
SHADER_VARIANT_PRIMITIVE_POINTS,
SHADER_VARIANT_ATTRIBUTES,
SHADER_VARIANT_ATTRIBUTES,
SHADER_VARIANT_ATTRIBUTES_POINTS
};
RID shader_variant = shader.canvas_shader.version_get_shader(shader.default_version, shader_variants[j]);
shader.pipeline_variants.variants[i][j].setup(shader_variant, fb_format, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0);
}
}
shader.default_version_rd_shader = shader.canvas_shader.version_get_shader(shader.default_version, 0);
}
{ //shadow rendering
Vector<String> versions;
versions.push_back(String()); //no versions
shadow_render.shader.initialize(versions);
{
Vector<RD::AttachmentFormat> attachments;
RD::AttachmentFormat af_color;
af_color.format = RD::DATA_FORMAT_R32_SFLOAT;
af_color.usage_flags = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
attachments.push_back(af_color);
shadow_render.framebuffer_fix_format = RD::get_singleton()->framebuffer_format_create(attachments);
RD::AttachmentFormat af_depth;
af_depth.format = RD::DATA_FORMAT_D24_UNORM_S8_UINT;
af_depth.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_X8_D24_UNORM_PACK32, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_X8_D24_UNORM_PACK32 : RD::DATA_FORMAT_D32_SFLOAT;
af_depth.usage_flags = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
attachments.push_back(af_depth);
shadow_render.framebuffer_format = RD::get_singleton()->framebuffer_format_create(attachments);
}
//pipelines
Vector<RD::VertexDescription> vf;
RD::VertexDescription vd;
vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
vd.location = 0;
vd.offset = 0;
vd.stride = sizeof(float) * 3;
vf.push_back(vd);
shadow_render.vertex_format = RD::get_singleton()->vertex_format_create(vf);
shadow_render.shader_version = shadow_render.shader.version_create();
for (int i = 0; i < 3; i++) {
RD::PipelineRasterizationState rs;
rs.cull_mode = i == 0 ? RD::POLYGON_CULL_DISABLED : (i == 1 ? RD::POLYGON_CULL_FRONT : RD::POLYGON_CULL_BACK);
RD::PipelineDepthStencilState ds;
ds.enable_depth_write = true;
ds.enable_depth_test = true;
ds.depth_compare_operator = RD::COMPARE_OP_LESS;
shadow_render.render_pipelines[i] = RD::get_singleton()->render_pipeline_create(shadow_render.shader.version_get_shader(shadow_render.shader_version, 0), shadow_render.framebuffer_format, shadow_render.vertex_format, RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
}
shadow_render.shader_fix.initialize(versions);
shadow_render.shader_fix_version = shadow_render.shader_fix.version_create();
shadow_render.shader_fix_pipeline = RD::get_singleton()->render_pipeline_create(shadow_render.shader_fix.version_get_shader(shadow_render.shader_fix_version, 0), shadow_render.framebuffer_fix_format, RD::INVALID_ID, RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
}
{ //bindings
bindings.id_generator = 0;
//generate for 0
bindings.default_empty = request_texture_binding(RID(), RID(), RID(), VS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, VS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, RID());
{ //state allocate
state.canvas_state_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(State::Buffer));
state.lights_uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(LightUniform) * MAX_RENDER_LIGHTS);
}
}
{
//polygon buffers
polygon_buffers.last_id = 1;
}
{ // default index buffer
PoolVector<uint8_t> pv;
pv.resize(6 * 4);
{
PoolVector<uint8_t>::Write w = pv.write();
int *p32 = (int *)w.ptr();
p32[0] = 0;
p32[1] = 1;
p32[2] = 2;
p32[3] = 0;
p32[4] = 2;
p32[5] = 3;
}
shader.quad_index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv);
shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 6);
}
{ //primitive
primitive_arrays.index_array[0] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 1);
primitive_arrays.index_array[1] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 2);
primitive_arrays.index_array[2] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 3);
primitive_arrays.index_array[3] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 6);
}
{ //default skeleton buffer
shader.default_skeleton_uniform = RD::get_singleton()->uniform_buffer_create(sizeof(SkeletonUniform));
SkeletonUniform su;
_update_transform_2d_to_mat4(Transform2D(), su.skeleton_inverse);
_update_transform_2d_to_mat4(Transform2D(), su.skeleton_transform);
RD::get_singleton()->buffer_update(shader.default_skeleton_uniform, 0, sizeof(SkeletonUniform), &su);
}
{ //default material uniform set
Vector<RD::Uniform> default_material_uniforms;
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
u.binding = 2;
u.ids.push_back(shader.default_skeleton_uniform);
default_material_uniforms.push_back(u);
u.ids.clear();
u.type = RD::UNIFORM_TYPE_TEXTURE_BUFFER;
u.binding = 1;
u.ids.push_back(default_textures.default_multimesh_tb);
default_material_uniforms.push_back(u);
shader.default_skeleton_uniform_set = RD::get_singleton()->uniform_set_create(default_material_uniforms, shader.canvas_shader.version_get_shader(shader.default_version, SHADER_VARIANT_ATTRIBUTES), 2);
}
ERR_FAIL_COND(sizeof(PushConstant) != 128);
}
bool RasterizerCanvasRD::free(RID p_rid) {
if (canvas_light_owner.owns(p_rid)) {
CanvasLight *cl = canvas_light_owner.getornull(p_rid);
ERR_FAIL_COND_V(!cl, false);
light_set_use_shadow(p_rid, false, 64);
canvas_light_owner.free(p_rid);
//canvas state uniform set needs updating
if (state.canvas_state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state.canvas_state_uniform_set)) {
RD::get_singleton()->free(state.canvas_state_uniform_set);
}
} else if (occluder_polygon_owner.owns(p_rid)) {
occluder_polygon_set_shape_as_lines(p_rid, PoolVector<Vector2>());
occluder_polygon_owner.free(p_rid);
} else {
return false;
}
return true;
}
RasterizerCanvasRD::~RasterizerCanvasRD() {
//canvas state
if (state.canvas_state_buffer.is_valid()) {
RD::get_singleton()->free(state.canvas_state_buffer);
}
if (state.canvas_state_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(state.canvas_state_uniform_set)) {
RD::get_singleton()->free(state.canvas_state_uniform_set);
}
//bindings
{
free_texture_binding(bindings.default_empty);
//dispose pending
_dispose_bindings();
//anything remains?
if (bindings.texture_bindings.size()) {
ERR_PRINT("Some texture bindings were not properly freed (leaked canvasitems?");
const TextureBindingID *key = NULL;
while ((key = bindings.texture_bindings.next(key))) {
TextureBinding *tb = bindings.texture_bindings[*key];
tb->reference_count = 1;
free_texture_binding(*key);
}
//dispose pending
_dispose_bindings();
}
}
//shaders
RD::get_singleton()->free(shader.default_skeleton_uniform_set);
RD::get_singleton()->free(shader.default_skeleton_uniform);
shader.canvas_shader.version_free(shader.default_version);
//buffers
RD::get_singleton()->free(shader.quad_index_array);
RD::get_singleton()->free(shader.quad_index_buffer);
//pipelines don't need freeing, they are all gone after shaders are gone
//samplers
for (int i = 1; i < VS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) {
for (int j = 1; j < VS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) {
RD::get_singleton()->free(default_samplers.samplers[i][j]);
}
}
//textures
RD::get_singleton()->free(default_textures.white_texture);
RD::get_singleton()->free(default_textures.black_texture);
RD::get_singleton()->free(default_textures.normal_texture);
RD::get_singleton()->free(default_textures.aniso_texture);
RD::get_singleton()->free(default_textures.default_multimesh_tb);
}