Войти
ПрограммированиеФорумГрафика

Обёртки над GL-ными шейдерами - какие могут быть подходы? (3 стр)

Страницы: 1 2 3 4 Следующая »
#30
21:27, 22 авг. 2019

Suslik
> можно пойти дальше и отказаться ещё от кучи сущностей, например, от вершинных
> атрибутов. пока это мало кто делает
А потому что нормальной распаковки для запакованных типов данных все еще не завезли ни в GLSL, ни в HLSL.
Сейчас ubyte4 через IL можно автоматически распаковать в нормализованный float4. А как это сделать для StructuredBuffer ?


#31
21:46, 22 авг. 2019

MrShoor
> Сейчас ubyte4 через IL можно автоматически распаковать в нормализованный float4. А как это сделать для StructuredBuffer

vec3 normal = normals[gl_VertexID].xyz / 255.0f - vec3(0.5f);
покатит ли такое в продакшен коде? нет, потому что 0.1% производительности там важнее, чем лишних тысяча строчек легаси говнокода. а для демо-проекта — запросто.
#32
22:00, 22 авг. 2019

Fantom09
> пусть даже очень быстро,

причём тут скорость ? или тебя покусал один любить загрузки за 1 вызов?
push constants для cmd идут же - в одном коде ray-trace как раз они и используются вместо
UBO

> glGetActiveUniformsiv+GL_UNIFORM_OFFSET, получил офсеты всех полей UBO на этом
> вся попаболь закончилась.

это и есть рефлекшен почти - это если ты заранее знаешь что тебе нужно

#33
(Правка: 22:07) 22:06, 22 авг. 2019

=A=L=X=
Мне не нравится в твоем коде, что:
1. Атрибутами нужно рулить вручную (а это уже ни в какие ворота)
2. Сначала use, потом draw

На мой взгляд использование должно быть в духе:

prog.setAttributes(indexBuffer, vertexBuffer, instanceBuffer);
prog.setUniform("u_bounds", vec2(renderer.getWidth(), renderer.getHeight()) );
prog.setUniform("u_scale", Ball::circleRadius );
prog.draw( GL_TRIANGLES, bla bla bla );

Ну и если узкое место, и не хочется искать локейшн по строке, то твой вариант с наследованием:

+ Показать

Атрибуты выставлять вручную - зло. VertexBuffer должен содержать лейаут, а Program должен уметь маппить этот лейаут на свои атрибуты.

Что-то в духе:

struct LayoutField {
  uint componentType
  uint componentCount;
  uint offset;
  uint normalize;
};

struct VBLayout {
  std::vector<LayoutField> fields;
  uint strideSize;
};

template <class vertexType>
class VertexBuffer {
  const VBLayout& Layout() const;
};

И задавать эти VBLayout-ы удобно вместе со структурой:

struct MyVertex {
  static const VBLayout& Layout() const;
};

#34
22:39, 22 авг. 2019

innuendo
> и используются вместо UBO
Не буду спорить, ты на этих пушконстантах уже второй год собаку ешь))

Просто с одной стороны - есть возможность быстро передать их в шейдер, с другой - нет необходимости вообще их передавать. Что лучше - нужно рассматривать в контексте конкретной задачи. Какая польза от пуш констант для вывод скажем так 100***** инстансов через vkCmdDrawIndirect? Опять же - не буду спорить, но мне почему-то кажется что один вызов vkCmdDrawIndirect будет существеннее быстрее 100***** вызовов vkCmdDraw с мега быстрыми пуш константами, особенно когда объем передаваемых данных исчисляется десятками мегабайт, что в принципе нельзя засунуть в пушконстанты.
Кстати, удивительно что до сих пор тут нет отдельной темы, с обсуждением этих самых пушконстант)))

innuendo
> это и есть рефлекшен почти - это если ты заранее знаешь что тебе нужно
Ну да, никто ж и не спорит, вопрос был о проблемах с выравниванием, а эта "проблема" решается в 10 строчек, а раз она решается - это не проблема. Попаболь возникает только если пытаться вручную вычислить все оффсеты согласно спецификации)

#35
22:50, 22 авг. 2019

Fantom09
> > и используются вместо UBO
> Не буду спорить, ты на этих пушконстантах уже второй год собаку ешь))

это один вендор использует

> Просто с одной стороны - есть возможность быстро передать их в шейдер

ну сколько можно про скорость ... где там написано, что быстрая передача ?

ты загнал один раз небольшие данные в cmd и потом используешь для draw dispatch
по идее драйвер лучше понимает в отличии от map/unmap

> вопрос был о проблемах с выравниванием, а эта "проблема" решается в 10 строчек

это если просты типы в cbuffer/ubo а если массив struct MaterialLayerInfo ?
у меня даже на DX11 руки опустились как это всё парсить через d3dreflection

> Кстати, удивительно что до сих пор тут нет отдельной темы, с обсуждением этих
> самых пушконстант)))

нуно сделать

#36
22:59, 22 авг. 2019

Fantom09

насколько я понимаю поинт push constans - они хранятся не в глобальной памяти GPU, а в локальной памяти шейдера

#37
(Правка: 23:22) 23:21, 22 авг. 2019

innuendo
> ну сколько можно про скорость ... где там написано, что быстрая передача ?
Вообще-то в спецификации)

Note
Push constants represent a high speed path to modify constant data in pipelines that is expected to outperform memory-backed resource updates.

innuendo
> ты загнал один раз небольшие данные в cmd и потом используешь для draw dispatch
> по идее драйвер лучше понимает в отличии от map/unmap
Если речь идет о сравнении юниформ с пушкнстантами - то да, однозначно лучше. Но читай предыдущие посты - сейчас вообще нет смысла юзать юниформы (читай - передавать пачку данных каждому объекту на каждом кадре), потому сравнение немного некорректное. Это примерно как сравнить iPhone10 с Nokia 3310.

innuendo
> а если массив struct MaterialLayerInfo
И в чем проблема? Там просто к именам полей добавляется имя переменной и индексы массива.
Я это проверял много лет назад, и если ничего не поломали, то должно работать. Пример писать лень, потому нагуглил результат вывода:

struct GerstnerParams
{
     vec2      cst_size; 
     vec2      cst_k;    
     float     cst_A;    
     float     cst_w;   
     float     cst_phi; 
     float     private; 
};


layout(std140) uniform Gerstners
{
     GerstnerParams cst_gerstners[MAX_LAYERS];
};

Для такого набора данных glGetActiveUniformBlockName+GL_UNIFORM_OFFSET вернет имена и смещения в таком вот виде:
Uniform block "Gerstners":
  0000: 35664 cst_gerstners[0].cst_size
  0008: 35664 cst_gerstners[0].cst_k
  0016:  5126 cst_gerstners[0].cst_A
  0020:  5126 cst_gerstners[0].cst_w
  0024:  5126 cst_gerstners[0].cst_phi
  0028:  5126 cst_gerstners[0].private
  0032: 35664 cst_gerstners[1].cst_size
  0040: 35664 cst_gerstners[1].cst_k
  0048:  5126 cst_gerstners[1].cst_A
  0052:  5126 cst_gerstners[1].cst_w
  0056:  5126 cst_gerstners[1].cst_phi
  0060:  5126 cst_gerstners[1].private
  0064: 35664 cst_gerstners[2].cst_size
.......................
Это немного сбивает с толку, но оно работает.

#38
23:29, 22 авг. 2019

Fantom09
> Для такого набора данных glGetActiveUniformBlockName

это когда ты априори знаешь что там лежит - смысл reflection что ты, не зная о структуре, вытаскиваешь метаданные

так то любой сможешь сделать :)

#39
23:32, 22 авг. 2019

Fantom09
> ush constants represent a high speed path to modify constant data in pipelines

ну тут же немного больше же чем просто загрузка :)

#40
23:47, 22 авг. 2019

innuendo
> это когда ты априори знаешь что там лежит
В смысле?

Я гружу неизвестный движку шейдер, дальше получаю из него список активных блоков юниформ, потом прохожу по этим блокам и для каждого из них вытаскиваю его имя, смещение и тип данных. На основе этих данных заполняю метаданные. для шейдерной программы. На основе этих метаданных создается список объектов UBO, к каждому из которых привязан свой буфер UBO. На этом все.

Остальное уже возлагается на пользователя и движок. Тут либо пользователь может сам изменить любое значение в буфере, типа: Program.getUBO("Gerstners")->set("cst_size", 0, 1)
В этом случае движок лишь устраняет "проблему" расчета смещений - по метаданным вычисляется смещение в буфере и по этому адресу записывается необходимое значение.
Либо же движок парсит эти данные, находит "знакомые" константы, и пробует отмаппить их на свою логику, типа заполнить структуру MVP и прочее.

#41
(Правка: 23 авг. 2019, 0:04) 23:49, 22 авг. 2019

Fantom09
> Я гружу неизвестный движку шейдер, дальше получаю из него список активных
> блоков юниформ

да, точно ... это меня переклинило
давно не работал с убогим апи

#42
(Правка: 11:19) 11:15, 23 авг. 2019

MrShoor
> prog.setUniform("u_bounds", vec2(renderer.getWidth(), renderer.getHeight()) );
> Ну и если узкое место, и не хочется искать локейшн по строке, то твой вариант с
> наследованием:

Тут дело далеко не столько в "узком месте", но в как можно большей типизации и статическом контроле.
У меня изначально тоже было неидеально.
Сейчас немного переделал следующим образом - обощённый класс Uniform напичканный всеми возможными set2f и arr3i используется как универсальный обобщённый элемент в контейнере map< Uniform > программы для того чтобы после компиляции и линковки автоматически его заполнить и иметь возможность делать так:

prog.uniform( "u_bounds" ).set2f( renderer.getWidth(), renderer.getHeight() );
однако оно мне изначально не нравилось не только скоростью, но и тем что легко можно перепутать из кода имена и ничего не понять до рантайма, а так же путать передаваемые типы с очень нестрогим и лишь частичным контролем - легко можно, например, в ivec3 пихать vec2 и ничего не заметить.
Поэтому сам теперь не понимаю почему сразу не догадался, что хардкодные биндинги сами должны иметь правильную типизацию.
Т.е. наплодил еще кучу классов такого толка:
class UniformTyped
{
protected:
  GLint location = -1;
public:

  friend class Program;
};

struct UniformFloat: public UniformTyped
{
  void set( GLfloat v0 )
  { glUniform1f( location, v0 ); };
};

struct UniformFloatArray: public UniformTyped
{
  void set( GLsizei count, const GLfloat *v )
  { glUniform1fv( location, count, v ); };
};

struct UniformVec2: public UniformTyped
{
  void set( GLfloat v0, GLfloat v1 )
  { glUniform2f( location, v0, v1 ); };
};

struct UniformVec2Array: public UniformTyped
{
  void set( GLsizei count, const GLfloat *v )
  { glUniform2fv( location, count, v ); };
};
...
struct UniformIVec3: public UniformTyped
{
  void set( GLint v0, GLint v1, GLint v2 )
  { glUniform3i( location, v0, v1, v2 ); };
};

и теперь класс с хардкодными биндингами выглядит так:

struct RenderCopy: public Program
{
  UniformVec2 u_bounds;
  UniformVec2 u_texBounds;
  UniformVec4 u_coords;
  UniformVec4 u_texCoords;
  UniformInt u_texture;
  Attrib a_data;

  void postInit() override
  {
    bind( u_bounds, "u_bounds" );
    bind( u_texBounds, "u_texBounds" );
    bind( u_coords, "u_coords" );
    bind( u_texCoords, "u_texCoords" );
    bind( u_texture, "u_texture" );
    a_data = attrib( "a_data" );
  }
};
...
prog.u_bounds.set( 1, 2, 3 ); // error!!!
Т.е. прямые программные имена - это теперь строго типизированные биндинги к юниформам повторяющие то что находится в мапе.
Т.е. тут и невозможно указать из остального кода неправильное имя и невозможно передать неверное в него данное и при изменении данного в декларации сразу повсплывают все места в программе где нужны правки.
Повторюсь, что у меня сейчас modelless и materialless штуки и всё захардкожено что касается графики. Спрайты, тайлы, все дела. Поэтому мне именно это и нужно и прямой доступ к вертексным атрибутам в принципе не напрягает, т.к. единственный тут вертексный атрибут - a_data это 4 точки от 0,0 до 1,0
Т.е. вы тут все правильно всё говорите насчёт агрегации вершин в какие то тоже агрегатные вещи, но просто лично мне это пока не нужно.
А вот типизация - это хорошо.

#43
11:20, 23 авг. 2019

Всё-таки охота сделать какой то докомпиляционный генератор из файлов шейдеров создающий эти классы-обёртки для совсем уж простоты и полного самоконтроля.... но пожалуй это уже будет оверкилл и затруднение в билде программы.

#44
13:28, 23 авг. 2019
    bind( u_bounds, "u_bounds" );
    bind( u_texBounds, "u_texBounds" );
    bind( u_coords, "u_coords" );
    bind( u_texCoords, "u_texCoords" );
    bind( u_texture, "u_texture" );
ещё макросов накрутить, чтобы вообще было
    bind( u_bounds );
    bind( u_texBounds );
    bind( u_coords );
    bind( u_texCoords );
    bind( u_texture );
али вообще X-Macro колдунство применить.

но это слишком олдскульно

Страницы: 1 2 3 4 Следующая »
ПрограммированиеФорумГрафика