--- /dev/null
+extension ARB_tessellation_shader
+#include <msp/gl/extensions/arb_tessellation_shader.h>
#include <msp/gl/extensions/msp_primitive_restart.h>
#include "batch_backend.h"
OpenGLBatch::OpenGLBatch(PrimitiveType t):
gl_prim_type(get_gl_primitive_type(t)),
gl_index_type(GL_UNSIGNED_SHORT)
-{ }
+{
+ if(t==PATCHES)
+ static Require _req(ARB_tessellation_shader);
+}
bool OpenGLBatch::check_restart(bool require)
{
#include <msp/gl/extensions/arb_shader_objects.h>
#include <msp/gl/extensions/arb_uniform_buffer_object.h>
#include <msp/gl/extensions/arb_vertex_array_object.h>
+#include <msp/gl/extensions/arb_tessellation_shader.h>
#include <msp/gl/extensions/ext_framebuffer_object.h>
#include <msp/gl/extensions/msp_primitive_restart.h>
#include "blend.h"
}
}
+ if(changes&PipelineState::PATCH_SIZE)
+ if(self.patch_size)
+ {
+ static Require _req(ARB_tessellation_shader);
+ glPatchParameteri(GL_PATCH_VERTICES, self.patch_size);
+ }
+
if(changes&PipelineState::FACE_CULL)
{
glFrontFace(self.front_face==CLOCKWISE ? GL_CW : GL_CCW);
case TRIANGLES: return GL_TRIANGLES;
case TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
case TRIANGLE_FAN: return GL_TRIANGLE_FAN;
+ case PATCHES: return GL_PATCHES;
default: throw invalid_argument("get_gl_primitive_type");
}
}
#include <msp/gl/extensions/arb_separate_shader_objects.h>
#include <msp/gl/extensions/arb_shader_objects.h>
#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include <msp/gl/extensions/arb_tessellation_shader.h>
#include <msp/gl/extensions/arb_vertex_shader.h>
#include <msp/gl/extensions/ext_gpu_shader4.h>
#include <msp/gl/extensions/khr_debug.h>
switch(type)
{
case VERTEX: { static Require _req(ARB_vertex_shader); gl_type = GL_VERTEX_SHADER; } break;
+ case TESS_CONTROL: { static Require _req(ARB_tessellation_shader); gl_type = GL_TESS_CONTROL_SHADER; } break;
+ case TESS_EVAL: { static Require _req(ARB_tessellation_shader); gl_type = GL_TESS_EVALUATION_SHADER; } break;
case GEOMETRY: { static Require _req(ARB_geometry_shader4); gl_type = GL_GEOMETRY_SHADER; } break;
case FRAGMENT: { static Require _req(ARB_fragment_shader); gl_type = GL_FRAGMENT_SHADER; } break;
case COMPUTE: { static Require _req(ARB_compute_shader); gl_type = GL_COMPUTE_SHADER; } break;
switch(st)
{
case SL::Stage::VERTEX: stage_id = add_stage(VERTEX); break;
+ case SL::Stage::TESS_CONTROL: stage_id = add_stage(TESS_CONTROL); break;
+ case SL::Stage::TESS_EVAL: stage_id = add_stage(TESS_EVAL); break;
case SL::Stage::GEOMETRY: stage_id = add_stage(GEOMETRY); break;
case SL::Stage::FRAGMENT: stage_id = add_stage(FRAGMENT); break;
case SL::Stage::COMPUTE: stage_id = add_stage(COMPUTE); break;
switch(e.stage)
{
case SpirVModule::VERTEX: stage_id = add_stage(VERTEX); break;
+ case SpirVModule::TESS_CONTROL: stage_id = add_stage(TESS_CONTROL); break;
+ case SpirVModule::TESS_EVAL: stage_id = add_stage(TESS_EVAL); break;
case SpirVModule::GEOMETRY: stage_id = add_stage(GEOMETRY); break;
case SpirVModule::FRAGMENT: stage_id = add_stage(FRAGMENT); break;
case SpirVModule::COMPUTE: stage_id = add_stage(COMPUTE); break;
void OpenGLProgram::set_stage_debug_name(unsigned stage_id, Stage type)
{
#ifdef DEBUG
- static const char *const suffixes[] = { " [VS]", " [GS]", " [FS]", " [CS]" };
+ static const char *const suffixes[] = { " [VS]", " [TCS]", " [TES]", " [GS]", " [FS]", " [CS]" };
string name = debug_name+suffixes[type];
glObjectLabel(GL_SHADER, stage_id, name.size(), name.c_str());
#else
enum Stage
{
VERTEX,
+ TESS_CONTROL,
+ TESS_EVAL,
GEOMETRY,
FRAGMENT,
COMPUTE,
void finalize_uniforms();
bool is_compute() const { return stage_ids[COMPUTE]; }
+ bool has_tessellation() const { return stage_ids[TESS_CONTROL] && stage_ids[TESS_EVAL]; }
void set_debug_name(const std::string &);
void set_stage_debug_name(unsigned, Stage);
{
Graphics::VulkanOptions opts;
opts.enable_geometry_shader = true;
+ opts.enable_tessellation_shader = true;
#ifdef DEBUG
const char *disable_ptr = getenv("MSPGL_DISABLE_VALIDATION");
if(disable_ptr && *disable_ptr)
switch(stage)
{
case SpirVModule::VERTEX: return VK_SHADER_STAGE_VERTEX_BIT;
+ case SpirVModule::TESS_CONTROL: return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
+ case SpirVModule::TESS_EVAL: return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
case SpirVModule::GEOMETRY: return VK_SHADER_STAGE_GEOMETRY_BIT;
case SpirVModule::FRAGMENT: return VK_SHADER_STAGE_FRAGMENT_BIT;
case SpirVModule::COMPUTE: return VK_SHADER_STAGE_COMPUTE_BIT;
}
constexpr unsigned graphics_mask = PipelineState::FRAMEBUFFER|PipelineState::VERTEX_SETUP|PipelineState::FACE_CULL|
- PipelineState::DEPTH_TEST|PipelineState::STENCIL_TEST|PipelineState::BLEND|PipelineState::PRIMITIVE_TYPE;
+ PipelineState::DEPTH_TEST|PipelineState::STENCIL_TEST|PipelineState::BLEND|PipelineState::PRIMITIVE_TYPE|
+ PipelineState::PATCH_SIZE;
unsigned pipeline_mask = PipelineState::SHPROG;
if(!self.shprog->is_compute())
pipeline_mask |= graphics_mask;
++n_color_attachments;
}
- StructureBuilder sb(buffer, 10);
+ bool has_tessellation = (self.shprog && self.shprog->has_tessellation());
+
+ StructureBuilder sb(buffer, 12);
VkGraphicsPipelineCreateInfo *const &pipeline_info = sb.add<VkGraphicsPipelineCreateInfo>();
VkPipelineInputAssemblyStateCreateInfo *const &input_assembly_info = sb.add<VkPipelineInputAssemblyStateCreateInfo>();
+ VkPipelineTessellationStateCreateInfo *const &tessellation_info = sb.add<VkPipelineTessellationStateCreateInfo>(has_tessellation);
+ VkPipelineTessellationDomainOriginStateCreateInfo *const &tess_origin_info = sb.add<VkPipelineTessellationDomainOriginStateCreateInfo>(has_tessellation);
VkPipelineViewportStateCreateInfo *const &viewport_info = sb.add<VkPipelineViewportStateCreateInfo>();
VkPipelineRasterizationStateCreateInfo *const &raster_info = sb.add<VkPipelineRasterizationStateCreateInfo>();
VkPipelineMultisampleStateCreateInfo *const &multisample_info = sb.add<VkPipelineMultisampleStateCreateInfo>();
input_assembly_info->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_info->topology = static_cast<VkPrimitiveTopology>(get_vulkan_primitive_type(self.primitive_type));
- input_assembly_info->primitiveRestartEnable = true;
+ input_assembly_info->primitiveRestartEnable = !has_tessellation;
+
+ if(has_tessellation)
+ {
+ tessellation_info->sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
+ tessellation_info->pNext = tess_origin_info;
+ tessellation_info->patchControlPoints = self.patch_size;
+
+ tess_origin_info->sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO;
+ tess_origin_info->domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
+ }
viewport_info->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_info->viewportCount = 1;
pipeline_info->sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info->pInputAssemblyState = input_assembly_info;
- pipeline_info->pTessellationState = 0;
+ pipeline_info->pTessellationState = (has_tessellation ? tessellation_info : 0);
pipeline_info->pViewportState = viewport_info;
pipeline_info->pRasterizationState = raster_info;
pipeline_info->pMultisampleState = multisample_info;
case TRIANGLES: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case TRIANGLE_STRIP: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
case TRIANGLE_FAN: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
+ case PATCHES: return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
default: throw invalid_argument("get_vulkan_primitive_type");
}
}
return stage_flags&VK_SHADER_STAGE_COMPUTE_BIT;
}
+bool VulkanProgram::has_tessellation() const
+{
+ constexpr unsigned tess_flags = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
+ return (stage_flags&tess_flags)==tess_flags;
+}
+
void VulkanProgram::set_debug_name(const string &name)
{
#ifdef DEBUG
void finalize_uniforms();
bool is_compute() const;
+ bool has_tessellation() const;
void set_debug_name(const std::string &);
void set_vulkan_object_name() const;
mark_dirty();
}
+void Batch::set_patch_size(unsigned s)
+{
+ if(prim_type!=PATCHES)
+ throw invalid_operation("Batch::set_patch_size");
+ if(s<1)
+ throw invalid_argument("Batch::set_patch_size");
+
+ patch_size = s;
+}
+
Batch &Batch::append(unsigned i)
{
append_index(i);
DataFile::ObjectLoader<Batch>(b)
{
add("indices", &Loader::indices);
+ add("patch_size", &Loader::patch_size);
}
void Batch::Loader::indices(const vector<unsigned> &ind)
obj.append(ind);
}
+void Batch::Loader::patch_size(unsigned s)
+{
+ obj.set_patch_size(s);
+}
+
} // namespace GL
} // namespace Msp
Loader(Batch &);
private:
void indices(const std::vector<unsigned> &);
+ void patch_size(unsigned);
};
private:
DataType index_type;
std::vector<std::uint8_t> data;
unsigned max_index;
+ unsigned patch_size = 3;
public:
Batch(PrimitiveType);
DataType get_index_type() const { return index_type; }
+ /** Sets the number of control points per patch. Only available if the
+ primitive type is PATCHES. */
+ void set_patch_size(unsigned);
+
+ unsigned get_patch_size() const { return patch_size; }
+
/** Appends a single index. The data type is automatically adjusted if the
index is too large for the current data type. */
Batch &append(unsigned);
enum Stage
{
VERTEX = 0,
+ TESS_CONTROL = 1,
+ TESS_EVAL = 2,
GEOMETRY = 3,
FRAGMENT = 4,
COMPUTE = 5
set(primitive_type, t, PRIMITIVE_TYPE);
}
+void PipelineState::set_patch_size(unsigned s)
+{
+ set(patch_size, s, PATCH_SIZE);
+}
+
void PipelineState::set_front_face(FaceWinding w)
{
set(front_face, w, FACE_CULL);
DEPTH_TEST = 256,
STENCIL_TEST = 512,
BLEND = 1024,
- PRIMITIVE_TYPE = 2048
+ PRIMITIVE_TYPE = 2048,
+ PATCH_SIZE = 4096
};
const Framebuffer *framebuffer = 0;
std::vector<BoundResource> resources;
const VertexSetup *vertex_setup = 0;
PrimitiveType primitive_type = TRIANGLES;
+ unsigned patch_size = 0;
FaceWinding front_face = COUNTERCLOCKWISE;
CullMode face_cull = NO_CULL;
DepthTest depth_test;
public:
void set_vertex_setup(const VertexSetup *);
void set_primitive_type(PrimitiveType);
+ void set_patch_size(unsigned);
void set_front_face(FaceWinding);
void set_face_cull(CullMode);
void set_depth_test(const DepthTest &);
pt = TRIANGLE_STRIP;
else if(conv.get()=="TRIANGLE_FAN")
pt = TRIANGLE_FAN;
+ else if(conv.get()=="PATCHES")
+ pt = PATCHES;
else
throw lexical_error(format("conversion of '%s' to PrimitiveType", conv.get()));
}
LINE_STRIP,
TRIANGLES,
TRIANGLE_STRIP,
- TRIANGLE_FAN
+ TRIANGLE_FAN,
+ PATCHES
};
void operator>>(const LexicalConverter &, PrimitiveType &);
public:
using ProgramBackend::is_compute;
+ using ProgramBackend::has_tessellation;
ReflectData::LayoutHash get_uniform_layout_hash() const { return reflect_data.layout_hash; }
unsigned get_n_descriptor_sets() const { return reflect_data.n_descriptor_sets; }
batch.refresh(frame_index);
PipelineState &ps = get_pipeline_state();
ps.set_primitive_type(batch.get_type());
+ if(batch.get_type()==PATCHES)
+ ps.set_patch_size(batch.get_patch_size());
commands.use_pipeline(&ps);
}