Если называть ЯВУ все что не является ассемблером(например си): на ЯВУ есть свой кайф при программировании. Например если твой код не зависит от внешних библиотек/фреймворков это тоже кайф.
А вот тут Intel был ни при чём. Это был типичный ассемблерный ночной баг. Нашёл, исправил. Мне одному не дают спать Вулкан и ассемблер по ночам?
Нет я тоже по всем ночам не сплю - всё жду когда вы мне, под мой музейный экспонат процессора, код на MASM64 - для OpenGL напишите.
Раз обещал - нехорошо заставлять ждать. Вообще я хотел сначала закончить парсер obj -> VBO, а потом перенести его в OpenGL-рамку, но если Вас терзает бессонница, то придётся отвлечься
GRAFik, у вас есть хотябы 1 проект, который вы сами написали и выложили на васме, пусть даже при помощи ллм?
Я бы не отвлекался на этого сетевого хомяка Он же вам деньги не платит? Какое имеет моральное право критиковать и отвлекать от творчества/лезть не в свое дело?
Application, что за менторская манера общения? Модераторская власть что-ли отпечаток накладывает? И на брудершафт мы с вами, вроде, не пили. Так, что повежливее нужно быть с своими зарегистрированными пользователями/форумчанами - их у вас и так, как кот наплакал...
Здесь наши интересы совпадают: мой движок OpenGL основан на технологиях прошлого века (OpenGL 1.0). Работать с реальными 3D-моделями он просто не способен. Поэтому мне нужно прикрутить к нему парсер VAO/VBO/EBO. Если при этом он кому-то будет полезен, то я только буду рад. Vulkan - штука нестабильная и требует наличия библиотеки. OpenGL под Windows запускается из коробки. У самурая нет цели, есть только путь. Но я иду в сторону своего IFC-ридера, а возможно, и редактора. Мы живём в интересное время. Сейчас я один могу делать столько же, сколько 30 лет назад делал весь Autodesk или Graphisoft (в плане разработки, не маркетинга). Поэтому мне не жалко написать один модуль, чтобы человеку стало весело. Я не гонюсь за авторским правом, потому что всё это придумано не мной и давно написано, я учусь сам и показываю другим людям.
Альтернативное времяпрепровождение - это прикольные видосики в телефоне. В лучшем случае просто тупые, в худшем - пропагандистские. Я выбрал другой путь. Так узнаю много нового (технологически нового) и держу мозг в тонусе. И я не знаю, где пригодятся мои знания в будущем. Уже сейчас я держу в руках связку WinAPI-Vulkan-GLSL, причём на чистом ассемблере, без третьих библиотек. Считаю матрицы преобразований с помощью AVX и FMA. В реальной жизни это мне никогда не пригодится. Почти наверное. Никто даже не знает, что я это умею. А что если я по приколу напишу свой мини-Revit, который будет соответствовать ГОСТам? Тупо чтобы потроллить Нанософт и Аскон? Задача по линии VK и OpenGL сейчас единая - мне нужен парсер obj -> VBO, причём не просто какой-нибудь, а хороший, годный - построенный на векторных инструкциях. Когда будет парсер, будет и визуализация
ml64, мне кажется, что на картинке девочка-подросток с большой грудью или у «Москвича» размер «Чайки»? размеры«Чайка»«Москвич 408»длина5600 мм4090 ммширина2000 мм1550 ммвысота1620 мм1480 ммколёсная база3250 мм2400 мм
А мне дед старые фотографии показывал, когда были в моде "инвалидки", как в старой комедии "самогонщики". Вот такую бы подкрасить, подсветить и с такой девушкой сфотографировать.
Точно, именно мотоколяска. У вас нет такой в вашей кодовой базе ? Тогда сразу оформляем спецзаказ, с полной предоплатой. Девушки только могут подвести. Они на такой позор могут не подписаться - ни за какие деньги.
GRAFik, «Операция Ы» — мотоколяска СМЗ С-3А «Кавказская пленница» — немецкий Adler Triumpf Junior 1937 года
Однозначно - да. На ЯВУ - проще. Но cmd = parts(i) - это действия, которые повторяются каждый байт (при каждом проходе): 1) вычисление адреса parts - это два лишних обращения к памяти: lea rax,parts add parts,i 2) весь перебор if cmd == 'v' и др. - это побайтовый опрос. Это долго. Для "Москвича" это не критично. Но мои объекты - это здания из огромного количества полигонов. Здесь нужны векторные возможности процессора, например, VPCMPISTRI Эта инструкция поможет определить, есть ли в строке нужные токены, а дальше можно уже по байтам разбирать, как с ними работать. Над чем я сейчас работаю: Сколько памяти выделить для буфера вершин? Можно взять с запасом, но это только для "Москвича" годится. Значит, надо считать. Как считать? Ищем строки, которые начинаются с "v". Но это могут быть и "v" с пробелом, и "v" с Tab, и "vn", и "vt". Как мы ищем? С помощью CMPSB? Нет, конечно. CMPSD? Но она сравнивает последовательность байт с последовательностью байт Берём VPCMPISTRI. Она сравнивает независимые 16 байт образца с последовательными 16 байтами памяти и ставит флаги, если находит первый совпавший байт. Очень удобно искать конец строки, если токен уже отработан или не нужен. Это база, а дальше - на любителя. Можно первым проходом заменить весь Tab на пробелы, можно удалить ведущие пробелы в начале строк, можно этого не делать, а просто посчитать количество токенов каждого типа и под них выделить нужное количество буферной памяти, можно идти в лоб и писать сразу в буфер VBO, но тогда получится монолитный объект, и я не смогу, например, открывать капот и двери "Москвича" Так что ЯВУ - это хорошо, но это не наш метод. Если бы я хотел просто и быстро крутить модель, я открыл бы её в SketchUp А мне интересен сам механизм.
Думал я думал - что это? Даже VPCMPISTRI посмотрел в сети - ничего не понял. Код (ASM): Mov eDi, Начальный Адрес Mov eCx, Сколько ClD / StD ; - направление Mov Al, 'v' ; что RepNE ScasB MovZx eBx, Byte ptr [eDi] BT [BitTab], eBx ; по битмап допустимых после 'v' байт ??
SCASB обращается к памяти каждый раз. Точнее, сейчас уже Intel это как-то кэширует (именно "как-то", т.к. это делает их тайный микрокод). Рассмотрим конкретный пример. Пример возьмём попроще - из широко известного в узких кругах пакета tinyobj_loader - см. вложение. Мне нужно узнать, сколько выделять памяти под буфер, т.е. сколько в исходном .obj-файле вершин, нормалей, полигонов, объектов, групп и прочих элементов. Моя задача - не найти символ "v" в строке, а подсчитать количество токенов "v", "vn", "vt", "o", "g" и т.д. в двух файлах: "геометрия.obj" и "материалы.mat". Причём даже встроенный в 3ds Max конвертер сохраняет данные, мягко говоря, не совсем удобно. Он может добавить Tab в начало строки, а может не добавить, он может задать вершины, а только потом объявить токен "o" ("объект"), так, что непонятно, к какому объекту эти вершины потом отнести. Мой алгоритм: Я беру первый значащий символ в строке, сравниваю его с пробелом, табом, LF, CR, EoF, # (комментарий в формате obj) - для каждого случая свой обработчик. Всё. Я определил, что символ значащий, дальше его обрабатываю. Если "o", то увеличиваю счётчик объектов, если "v", то увеличиваю счётчик вершин и т.д. А теперь представьте, что я для этого использую SCASB. 1) Мне надо для каждого токена запускать свой цикл сканирования (а если файл 20 мегабайт? Это не редкость для современной графики) 2) а если v - это не токен, а часть имени объекта, например, o vase - объект "ваза"? Сейчас я просто ищу начало строки, беру в ней первый значащий символ и сравниваю байт (или слово) по адресу rcx с ASCII-кодом первого символа токена: Спойлер: Побитовое сканирование Код (ASM): countObjEntities proc LOCAL pCurrentPosition:QWORD,hHeap:QWORD LOCAL nObjectIndex:DWORD,nMaterialIndex:DWORD PROLOG 100h ;Set the Local Pointer mov rcx,gpObjDataStart mov pCurrentPosition,rcx ;Initialize Counters mov gnTotalObjects,0 mov gnTotalGroups,0 mov gnTotalSubGroups,0 mov gnTotalVertices,0 mov gnTotalNormals,0 mov gnTotalFaces,0 mov gnTotalMtlLibs,0 mov gnTotalIndices,0 ;Initialize Indices mov nObjectIndex,0 lbl_NextLine: mov rcx,pCurrentPosition cmp rcx,gpObjDataEnd jge lbl_EndOfFile ;Check for consistency mov al,byte ptr[rcx] cmp al,0 ;EOF je lbl_EndOfFile cmp al,9 ;Tab je lbl_SkipByte cmp al,0Ah ;CR je lbl_SkipByte cmp al,0Dh ;LF je lbl_SkipByte cmp al,20h ;Space je lbl_SkipByte cmp al,23h ;# OBJ Format Comment je lbl_SkipByte ;Token detection. Arranged by Probability ;mov rcx,pCurrentPosition cmp byte ptr[rcx],76h ;"v" je lbl_TokenStartsWithV cmp byte ptr[rcx],66h ;"f" je lbl_TokenFace cmp dword ptr[rcx],6D657375h ;"usem" in reverse order - part of "usemtl" je lbl_TokenUseMtl cmp byte ptr[rcx],6Fh ;"o" je lbl_TokenObject cmp byte ptr[rcx],67h ;"g" je lbl_TokenGroup cmp dword ptr[rcx],6C6C746Dh; "mtll" in reverse order - part of "mtllib" je lbl_TokenMtlLib jmp lbl_SkipByte ;Count object name: o name lbl_TokenObject: add pCurrentPosition,2 ;skip "o " inc gnTotalObjects jmp lbl_SkipByte ;Count object name: g name lbl_TokenGroup: add pCurrentPosition,2 ;skip "g " inc gnTotalGroups jmp lbl_SkipByte ;Count material: "usemtl" lbl_TokenUseMtl: add pCurrentPosition,7 ;skip "usemtl " inc gnTotalSubGroups jmp lbl_SkipByte ;Branch V lbl_TokenStartsWithV: inc pCurrentPosition cmp byte ptr[rcx],6Eh ;"n" je lbl_TokenNormal cmp byte ptr[rcx],74h ;"t" je lbl_TokenTextureCoordinate ;jmp lbl_TokenVertex ;By default ;Count vertex: v x y z lbl_TokenVertex: inc pCurrentPosition ;skip "v " inc gnTotalVertices jmp lbl_SkipByte ;Count normal: vn x y z lbl_TokenNormal: add pCurrentPosition,2 ;skip "vn " inc gnTotalNormals jmp lbl_SkipByte ;Count texture: vt u v w lbl_TokenTextureCoordinate: add pCurrentPosition,2 ;skip "vt " inc gnTotalTextureCoords jmp lbl_SkipByte ;Count face: f v/vt/vn v/vt/vn v/vt/vn lbl_TokenFace: inc pCurrentPosition ;skip "f " inc gnTotalFaces jmp lbl_SkipByte ;Count material: "mtllib" lbl_TokenMtlLib: add pCurrentPosition,7 ;skip "mtllib " inc gnTotalMtlLibs jmp lbl_SkipByte lbl_SkipByte: mov rcx,pCurrentPosition cmp rcx,gpObjDataEnd jge lbl_EndOfFile mov al,byte ptr[rcx] cmp al,0 ;EOF je lbl_EndOfFile cmp al,0Ah ;LF je lbl_SkipLF cmp al,0Dh ;CR je lbl_SkipCR inc pCurrentPosition jmp lbl_SkipByte lbl_SkipCR: inc pCurrentPosition mov rcx,pCurrentPosition cmp byte ptr[rcx],0Ah ;LF je lbl_SkipLF jmp lbl_NextLine lbl_SkipLF: inc pCurrentPosition jmp lbl_NextLine lbl_EndOfFile: cmp gnTotalObjects,0 jne @f LOG_TEXT szErrNoObjects @@: cmp gnTotalGroups,0 jne @f LOG_TEXT szErrNoGroups @@: cmp gnTotalSubGroups,0 jne @f LOG_TEXT szErrNoSubGroups @@: cmp gnTotalMtlLibs,0 jne @f LOG_TEXT szErrNoMtlLibs @@: cmp gnTotalVertices,0 jne @f LOG_TEXT szErrNoVertices @@: cmp gnTotalNormals,0 jne @f LOG_TEXT szErrNoNormals @@: cmp gnTotalTextureCoords,0 jne @f LOG_TEXT szErrNoTextureCoords @@: cmp gnTotalFaces,0 jne @f LOG_TEXT szErrNoFaces @@: ;Success mov rax,1 jmp lbl_End lbl_WinError: call SpellWinError xor rax,rax ;jmp lbl_End lbl_End: EPILOG countObjEntities endp Но это неудобно тем, что я для чтения каждого байта обращаюсь к памяти, гоняю этот несчастный байт по всем шинам и контроллерам. SSE предлагает хорошее решение - VPCMPISTRI: Я создаю в памяти шаблон поиска - 4, 8 или 16 байт для интересующих меня символов, т.е. для поиска начала строки и пропуска пустышек это 10h, 13h, 20h, 9, для обработки токенов это ASCII-коды o, g, v, m (первые буквы токенов). А дальше - дело техники: xmm0 = шаблон поиска, xmm1 или адрес в памяти - сканируемый участок, imm = маска (ANSI или Unicode и другие настройки). Как только VPCMPISTRI встречает в заданном диапазоне любой символ из шаблона, она возвращает в RCX смещение этого символа и устанавливает CF. Кстати, если она встречает в заданном диапазоне 0, то устанавливает ZF, что очень важно, т.к. можно проскочить EoF. Вот над этим я и работаю: массово сканирую в поисках 10h, 13h, 20h, 9, а дальше точечно обрабатываю значимые данные. Надеюсь, понятно разложил.