Cheb's Game Engine
Модератор: Модераторы
1) Матрица должна быть параметром процедуры рисования, а не полем меша, соответственно, все эти Ortho etc. — функциями, её возвращающими, а не методами левого класса.
2) Не допускаешь, что у некоторых мешей какие-то атрибуты могут отсутствовать? Тем же экранным квадам не нужны нормали, текстурированным в простых случаях (т. е. когда не предрассчитываешь освещение в вершинах) не нужны цвета.
3) У тебя уже и так готовые массивы, поэтому даже в FFP нет причины не использовать [Vertex/TexCoord/etc.]Pointer + DrawElements вместо цикла. А ещё лучше записать данные меша в VBO, которые и так поддерживаются вообще везде (OpenGL 1.4, 1.1 or so). Собственно, рисовать из оперативной памяти = нагружать CPU и шину ерундой.
4) Создавать меш, рисовать и тут же уничтожать — сам понимаешь, неочень.
5) Выставлять стейт перед каждым рисованием тоже неочень, будто сам как программист не знаешь, где у тебя что было включено.
2) Не допускаешь, что у некоторых мешей какие-то атрибуты могут отсутствовать? Тем же экранным квадам не нужны нормали, текстурированным в простых случаях (т. е. когда не предрассчитываешь освещение в вершинах) не нужны цвета.
3) У тебя уже и так готовые массивы, поэтому даже в FFP нет причины не использовать [Vertex/TexCoord/etc.]Pointer + DrawElements вместо цикла. А ещё лучше записать данные меша в VBO, которые и так поддерживаются вообще везде (OpenGL 1.4, 1.1 or so). Собственно, рисовать из оперативной памяти = нагружать CPU и шину ерундой.
4) Создавать меш, рисовать и тут же уничтожать — сам понимаешь, неочень.
5) Выставлять стейт перед каждым рисованием тоже неочень, будто сам как программист не знаешь, где у тебя что было включено.
Умудрился вынести самому себе мозг, чуть не потярял очки SAN разбирая свой фхтагнокод 2008-го года... В конце концов пришлость снести его на и написать с нуля.

Для консоли уже оптимизировал, остальные места модуля - матки - не критичны, буду оптимизироватоь на стороне игрового модуля:
Обязательно вернусь к этому когда дойду до стадии непадающей игровой логики.
С наскока ниасилил, получил чёрный экран при молчащей glGetError()
Это изначальный базовый для простых вещей.
Понадобится оптимизация - напложу специализированных потомков.
В норме да, но я делаю базу для использования старого говнокода с минимумом изменений.
На работе третий год подобным же занимаюсь, целый эмулятор Joomla! 1.5 написал.
Добавлено спустя 11 часов 30 минут 35 секунд:
Всё, разобрался. Это не лыжи не едут, это я... кхм.
Когда шлёшь видяхе четырёхмерные координаты (на... кой?) - четвёртую компоненту надо заполнять единицами. А не то.
FFP зачем-то множит три первые компоненты на 1/четвёртую.
Чую, предстоит курить матрицы.
По ходу, потестирую потом производительность этой штуки. Если окажется адекватной - буду рендерить ею все места, где надо быстро подмухлевать геометрию на лету, не дожидаясь ленивого обновления. Например, ближние чанки вокруг игрока, где надо шустро по отдельности переключать грани с общего текстурного атласа чанка на индивидуальные текстуры повышенного разрешения, или дорисовывать подстраховочные оболочки за ближними стенками на случай фейла по z-near.
Напомню, производительность классического glVertex3f(x,y,z) держится на вполне приличном уровне вплоть до 20К где-то полигонов, а потом - резко падает. Наверняка ж специальный хак с буфером в юзерспейсе для поддержки старых игр.
Ещё впечатлился реализацией прозрачности в Fallout 4 (на дизеринге).
http://chebmaster.com/_share/fo4-dithering.avi (4 Мб)
Облизываюсь, примеряюсь где буду такое использовать. Она ж без блендинга вообще, и порядок сортировки не важен!
Что если смещение матрицы смещать на пол-периода каждый кадр? Тогда получится отличное темпоральное сглаживание (не говоря уже, что я планирую искривлённый вывод супер-сэмпленного рендер-буфера для получения сферической проекции, что даст дополнительно сглаживание размытием)

Создавать меш, рисовать и тут же уничтожать — сам понимаешь, неочень.
Для консоли уже оптимизировал, остальные места модуля - матки - не критичны, буду оптимизироватоь на стороне игрового модуля:
Код: Выделить всё
procedure TConsole.Draw();
var
zoom: glFloat;
xmax, zms: integer;
condensed: boolean;
liT, Red, Green, Blue, Alpha: glFloat;
currentLineTop, currentYTop: integer;
//processing from bottom to top until the next line is completely off-screen
function DrawOneLine() : boolean;
var
xp, yp, x, y, c, cc, L2, y2, SubLines, tlen: integer;
ts, tt: GLfloat;
begin
if currentLineTop < 0 then Exit(false);
tlen:= Length(Self.Strings[currentLineTop]);
if (tlen > 1)
then SubLines:= 1 + ((tlen - 1 - 1) div (xmax))
else SubLines:= 1;
currentYTop -= Sublines * trunc(ConsoleCharHeight * zoom);
if (currentYTop + Sublines * ConsoleCharHeight * zoom) < 0 //completely off screen?
then Exit(false);
x:=0;
y:=0;
c:=1;
While c < tlen - 1 do begin
//draw character;
xp:= trunc(ConsoleBorders) + x * trunc(ConsoleCharWidth * zoom);
yp:= y * trunc(ConsoleCharHeight * zoom);
cc:=Ord(Self.Strings[currentLineTop][c]);
if cc >=32 then begin //ignore special characters
ts:=((cc mod 16) * ConsoleCharXstep + ConsoleCharOffsetS) / ConsoleImageWidth;
tt:=((cc div 16) * 16 + ConsoleCharOffsetT) / 256.0;
f_mesh.AddQuad2d(
Red, Green, Blue, Alpha,
xp, yp + currentYTop,
xp + (ConsoleCharWidth + 1) * zoom, yp + currentYTop + ConsoleCharHeight * zoom,
ts, tt, ts + (ConsoleCharWidth + 1) / ConsoleImageWidth, tt + ConsoleCharHeight / 256.0 );
end;
inc (c);
inc (x);
if x > xmax then begin
x:=1;
inc(y);
end;
end;
currentLineTop-= 1;
Result:= true;
end;
begin
if not Mother^.Display.CanRender then Exit;
zms:= Round((Now() - f_lasttime) * 1000.0 * SECONDS_PER_DAY );
LastHeartbeatMoment:=Now();
If not (f_vis
or (zms < (f_hidetime + f_fadetime)))
then begin
f_fadeline:= -1;
f_fade:= 1.0;
Exit;
end;
if f_vis then liT:=0
else begin
if zms < f_hidetime then liT:= - (f_hidetime - zms) / f_hidetime
else liT:=(zms - f_hidetime) / f_fadetime;
end;
if f_scrolled_back < 0 then f_scrolled_back:=0;
if not f_vis then f_scrolled_back:=0;
// f_fade:= 1.0;
Alpha:= CCLd(liT, 4) * Mother^.Display.FadeIn;
condensed:= ConsoleUseCondensedFont or (Mother^.Display.WindowClientRect.Width < f_tosquish);
if not Assigned(f_mesh)
or f_change.Yes
or (f_change.lit <> trunc(Alpha * 1000))
or (f_change.sb <> f_scrolled_back)
or (f_change.dw <> Mother^.Display.WindowClientRect.Width)
or (f_change.dh <> Mother^.Display.WindowClientRect.Height)
or (f_change.condensed <> condensed)
then begin
if Assigned(f_mesh) then begin f_mesh.Free; f_mesh:= nil end;
if Self.Count < 1 then Exit;
f_change.Yes := No;
f_change.dw := Mother^.Display.WindowClientRect.Width;
f_change.dh := Mother^.Display.WindowClientRect.Height;
f_change.sb := f_scrolled_back;
f_change.lit := trunc(Alpha * 1000);
f_change.condensed:= condensed;
Red:= CCLd(liT, 1);
Green:= CCLd(liT, 2);
Blue:= CCLd(liT, 3);
if condensed then begin
ConsoleCharWidth:= 4;
ConsoleCharXstep:= 128 div 16;
ConsoleImageWidth:= 128.0;
ConsoleCharOffsetS:= 2;
end
else begin
ConsoleCharWidth:= 9;
ConsoleCharXstep:= 256 div 16;
ConsoleImageWidth:= 256.0;
ConsoleCharOffsetS:= 4;
end;
zoom:=1.0;
if Mother^.Display.WindowClientRect.Width > f_tostretch then zoom:= 2.0;
xmax := -1 + ((Mother^.Display.WindowClientRect.Width
- trunc(2 * ConsoleBorders * zoom ) ) div trunc(zoom * ConsoleCharWidth));
currentYTop:= Mother^.Display.WindowClientRect.Height - trunc(ConsoleBorders);
currentLineTop:= max(0, Self.Count - 1 - f_scrolled_back);
if (currentLineTop < 1) and (f_scrolled_back > 0) then begin
f_scrolled_back:= max(0, f_scrolled_back - (1 - currentLineTop));
currentLineTop:= max(0, Self.Count - 1 - f_scrolled_back);
end;
f_mesh:= TBasicMesh.Create;
while DrawOneLine() do;
end;
if Assigned(f_mesh) then begin
SetGLStatesForGUI;
f_mesh.SetGLStatesForGUI;
if condensed
then glBindTexture(GL_TEXTURE_2D, f_halftexture)
else glBindTexture(GL_TEXTURE_2D, f_texture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
f_mesh.Render;
end;
end;
, поэтому даже в FFP нет причины не использовать [Vertex/TexCoord/etc.]Pointer + DrawElements вместо цикла.
Обязательно вернусь к этому когда дойду до стадии непадающей игровой логики.
С наскока ниасилил, получил чёрный экран при молчащей glGetError()
, что у некоторых мешей какие-то атрибуты могут отсутствовать?
Это изначальный базовый для простых вещей.
Понадобится оптимизация - напложу специализированных потомков.
, соответственно, все эти Ortho etc. — функциями, её возвращающими, а не методами левого класса.
В норме да, но я делаю базу для использования старого говнокода с минимумом изменений.
На работе третий год подобным же занимаюсь, целый эмулятор Joomla! 1.5 написал.
Добавлено спустя 11 часов 30 минут 35 секунд:
Всё, разобрался. Это не лыжи не едут, это я... кхм.
Когда шлёшь видяхе четырёхмерные координаты (на... кой?) - четвёртую компоненту надо заполнять единицами. А не то.
FFP зачем-то множит три первые компоненты на 1/четвёртую.
Чую, предстоит курить матрицы.
По ходу, потестирую потом производительность этой штуки. Если окажется адекватной - буду рендерить ею все места, где надо быстро подмухлевать геометрию на лету, не дожидаясь ленивого обновления. Например, ближние чанки вокруг игрока, где надо шустро по отдельности переключать грани с общего текстурного атласа чанка на индивидуальные текстуры повышенного разрешения, или дорисовывать подстраховочные оболочки за ближними стенками на случай фейла по z-near.
Напомню, производительность классического glVertex3f(x,y,z) держится на вполне приличном уровне вплоть до 20К где-то полигонов, а потом - резко падает. Наверняка ж специальный хак с буфером в юзерспейсе для поддержки старых игр.
Ещё впечатлился реализацией прозрачности в Fallout 4 (на дизеринге).
http://chebmaster.com/_share/fo4-dithering.avi (4 Мб)
Облизываюсь, примеряюсь где буду такое использовать. Она ж без блендинга вообще, и порядок сортировки не важен!
Что если смещение матрицы смещать на пол-периода каждый кадр? Тогда получится отличное темпоральное сглаживание (не говоря уже, что я планирую искривлённый вывод супер-сэмпленного рендер-буфера для получения сферической проекции, что даст дополнительно сглаживание размытием)
Последний раз редактировалось Cheb 16.04.2018 18:27:57, всего редактировалось 1 раз.
runewalsh писал(а):А ещё лучше записать данные меша в VBO
а если данные в меше пересчитываются регулярно (анимированный меш). То есть ли смсыл?
Cheb писал(а):Напомню, производительность классического glVertex3f(x,y,z) держится на вполне приличном уровне вплоть до 20К где-то полигонов, а потом - резко падает. Наверняка ж специальный хак с буфером в юзерспейсе для поддержки старых игр.
какой бы там бестробуфер не был задействован, ты как-минимум потеряешь на стоимости вызова функции.
Т.е. для какого-нить меша на 10К точек, у тебя вызовов с бесполезным перезаполнением стэка будет соотвественно 10*4к (вертекс/нормаль/текстура(ы)/цвет). Это становиться ещё большей бессмыслицей, если учесть, что сам точки в твоём коде хранятся в массиве же.
Кстати о классицизме. glVertex() был придуман в те времена, когда запросы к графике были скромными. И количество этих самых точек будет достаточно низким (да и процессор больше потратит времени на обсчёт сцены... fpu со-процессором
Но даже тогда, понимали, что вызывать тучу процедур, на каждый кадр, это не хорошо, и были придуманы диспленые списки.
Которые в последствии были успешно забракованы свежедобавленной поддержкой поддержкой массивов, ну и VBO, конечно, решает.
понимали, что вызывать тучу процедур, на каждый кадр, это не хорошо,
Вот, кстати, себе на заметку: покопаться в потрохах третьего квейка (он же Open Arena) и посмотреть, как это там.
а если данные в меше пересчитываются регулярно (анимированный меш).
По идее, рассчитывать анимацию на CPU - это зло. И это для меня - дальний этап, на после завершения первой игры (собственная нескелетная физикообразная анимация).
Пока же, видов анимированных мешей будет два, и оба укладываются в парадигму закачивания на видеокарту двух ключевых кадров, и смешивания их уже на видеокарте (передачей одного дополнительного коэффициента):
- чанки где что-нибудь течёт или сыплется. По парадигме ленивой физики, они просчитывают своё состояние на 200..4000 милисекунд вперёд, и засыпают. При чтении состояние чанка берётся интерполяцией между прошлой и будущей точками. При модификации внешней силою - актуализует промежуточное состояние и просчитывает заново уже от него.
- модели игроков в формате MD3 (Open Arena), с предрассчитанной, уже запечённой анимацией.
Комбинированый меш окружающих игрока чанков (12 штук) будет пересобираться каждый кадр, и только в его пределах (это где-то радиус 10 метров) будут возможны следующие фичи:
- любая полупрозрачность (воды, задымлённых блоков, партиклов)
- честная трава
-
Mirage
- энтузиаст
- Сообщения: 881
- Зарегистрирован: 06.05.2005 20:29:07
- Откуда: Russia
- Контактная информация:
Cheb писал(а):Облизываюсь, примеряюсь где буду такое использовать. Она ж без блендинга вообще, и порядок сортировки не важен!
Ты смотри, прям как в Анреале (первом) когда тот в софтовом режиме работал!
Еще недавно читал, что в свежем GTA такой прием используется, причем более адекватно смотрится, нежели в ролике.
Cheb писал(а):Что если смещение матрицы смещать на пол-периода каждый кадр? Тогда получится отличное темпоральное сглаживание (не говоря уже, что я планирую искривлённый вывод супер-сэмпленного рендер-буфера для получения сферической проекции, что даст дополнительно сглаживание размытием)
Скорее всего, выглядеть будет жутковато, но почему бы не попробовать...
Cheb писал(а):По парадигме ленивой физики, они просчитывают своё состояние на 200..4000 милисекунд вперёд, и засыпают. При чтении состояние чанка берётся интерполяцией между прошлой и будущей точками. При модификации внешней силою - актуализует промежуточное состояние и просчитывает заново уже от него.
А без внешних сил движение равномерное и прямолинейное?
А вообщше внешние силы практически всегда есть, получается, так будет тратиться куча ресурсов зря, просчитывая наперед.
А без внешних сил движение равномерное и прямолинейное?
А вообщше внешние силы практически всегда есть, получается, так будет тратиться куча ресурсов зря, просчитывая наперед.
Прямолинейное и равномерное.
Надо учитывать, что внешние силы - такие же ленивые, просчитывают своё будущее состояние с учётом интерполированного состояния чанка (т.к. в общем случае периоды не совпадают)
Из-за этого вот несовпадения периодов расчёт почти никогда не будет напрасным. Поскольку прерывание сна начинается с блендинга состояний на момент для текущего тика, какая-то часть рассчитанного изменения произойдёт.
Короче, вырожденный случай - обычная физика, рассчитываемая каждый тик.
такой прием используется, причем более адекватно смотрится, нежели в ролике.
Надо учитывать, что:
а) я увеличил видео вдвое чтобы лучше видно было
б) моя GTX460 не дотягивает до минимальных системных требований.
, но почему бы не попробовать...
А с обычной прозрачностью геморрой ещё тот. Меня, лично, бесит, когда при погружении в воду у персонажа исчезают волосы (чем грешат все игры от обливиона и до фолаутньювегаса)
Ея финальная форма
:
Код: Выделить всё
procedure TDumbUniMesh.Render;
var i: integer;
begin
if Length(indices) <= 0 then Exit;
if maxIndex < High(indices) then maxIndex:= High(indices);
case Mother^.GAPI.Mode of
{$ifndef glesonly}
gapi_DeprecatedImmediateGL, gapi_GL21: begin
{
glBegin(GL_TRIANGLES);
for i:=0 to maxIndex do begin
glColor4fv(@colors[indices[i]]);
glNormal3fv(@normals[indices[i]]);
glTexCoord3fv(@texcoords[indices[i]]);
glVertex4fv(@vertices[indices[i]]);
end;
glEnd;
}
// https://www.opengl.org/wiki/Client-Side_Vertex_Arrays
glGetError(); //***TODO this is a hack for an error state provoked somewhere else. Note to self: find and eliminate
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(4, GL_FLOAT, 0, @vertices[0]);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, 0, @colors[0]);
glClientActiveTexture(GL_TEXTURE0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(3, GL_FLOAT, 0, @texcoords[0]);
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 0, @normals[0]);
glDrawElements(GL_TRIANGLES, maxIndex + 1, GL_UNSIGNED_SHORT, @indices[0]);
CheckGLError;
end;
{$endif glesonly}
gapi_GLES2: begin
Die('***NotImplementedYet');
end;
else
DieUnsupportedGLMode;
end;
end; Разгрёб говнокоды до стадии компилируемости игрового модуля.
Теперь самое страшное: найти из-за какого из 100500 перелопаченных мест его развозит до полного выхода в атсрал.
По крайней мере, та часть Чеперси, что отвечает за регистрацию, выполняется без крахов.
Неприятным сюрпризам стало, что экземпляр класса потолстел на размер указателя (причём, втыкается это в конце после всех полей). Предполагаю, что это ссылка на интерфейс.
Грязный хакЪ для обхода:
Добавлено спустя 3 часа 28 минут 10 секунд:
Чудом увернулся от нежданчика, который бы мне выел весь мозг в будущем, заставив протрахаться с отладкой неделю, а может, и месяц. Каким чудом? При пробном запуске 64-битной версии модуля стукнула проверка, вставленная мной в Чеперси как бы ещё не в 2007-м, на то, как компилятор размещает в памяти счётчик ссылок массива. Она сама уже не актуальна, но привлекла моё внимание и оказалось:
Все мои ктулхаки мухлежа со счётчиком ссылок в строках не учитывали того факта, что оный счётчик может быть равен минус единице!
А -1 он равен у константных строк - т.е. тех, которые изначально зашиты в екзешник.
См.:
Это был бы полный.
Теперь самое страшное: найти из-за какого из 100500 перелопаченных мест его развозит до полного выхода в атсрал.
По крайней мере, та часть Чеперси, что отвечает за регистрацию, выполняется без крахов.
Неприятным сюрпризам стало, что экземпляр класса потолстел на размер указателя (причём, втыкается это в конце после всех полей). Предполагаю, что это ссылка на интерфейс.
Грязный хакЪ для обхода:
Код: Выделить всё
procedure TChepersyObject.RegisterFields;
begin
ListFields([
'CpsMask', @f_cpsmask, typeinfo(dword),
'{Skipped,NeverClone}f_RefCount', @f_refCount, typeinfo(dword)//typeinfo(longint)
//Can't use longint as it is not registered yet at this time
// while I have NO desire of touching the hard-coded type numbers used by
// the backward/forward compatibility mechanism to resolve unknown types
,'CpsWTFisThisCrapMaybeInterfacePointer', pointer(@f_refCount) + sizeof(f_refCount), CPS_POINTER
]);
end; Добавлено спустя 3 часа 28 минут 10 секунд:
Чудом увернулся от нежданчика, который бы мне выел весь мозг в будущем, заставив протрахаться с отладкой неделю, а может, и месяц. Каким чудом? При пробном запуске 64-битной версии модуля стукнула проверка, вставленная мной в Чеперси как бы ещё не в 2007-м, на то, как компилятор размещает в памяти счётчик ссылок массива. Она сама уже не актуальна, но привлекла моё внимание и оказалось:
Все мои ктулхаки мухлежа со счётчиком ссылок в строках не учитывали того факта, что оный счётчик может быть равен минус единице!
А -1 он равен у константных строк - т.е. тех, которые изначально зашиты в екзешник.
См.:
Код: Выделить всё
s1:= 'phthagn!'; //s1+= 'ы';
s2:= s1;
addlog('%0 %1', [ptrint(pointer(ptruint(s2) - sizeof(pointer))^), ptrint(pointer(ptruint(s2) - 2 * sizeof(pointer))^)]);
addlog('%0 %1 %2', [s2, pointer(s2), pointer(s2)] );
s2+= '#';
addlog('%0 %1', [ptrint(pointer(ptruint(s2) - sizeof(pointer))^), ptrint(pointer(ptruint(s2) - 2 * sizeof(pointer))^)]);
addlog('%0 %1 %2', [s2, pointer(s2), pointer(s2)] );
addlog('--');
s1:= 'phthagn!';
s1+= 'ы';
s2:= s1;
addlog('%0 %1', [ptrint(pointer(ptruint(s2) - sizeof(pointer))^), ptrint(pointer(ptruint(s2) - 2 * sizeof(pointer))^)]);
addlog('%0 %1 %2', [s2, pointer(s2), pointer(s2)] );
s2+= '#';
addlog('%0 %1', [ptrint(pointer(ptruint(s2) - sizeof(pointer))^), ptrint(pointer(ptruint(s2) - 2 * sizeof(pointer))^)]);
addlog('%0 %1 %2', [s2, pointer(s2), pointer(s2)] );
addlog('--');
s1:= IntToStr(9);
s2:= s1;
addlog('%0 %1', [ptrint(pointer(ptruint(s2) - sizeof(pointer))^), ptrint(pointer(ptruint(s2) - 2 * sizeof(pointer))^)]);
addlog('%0 %1 %2', [s2, pointer(s2), pointer(s2)] );
s2+= '#';
addlog('%0 %1', [ptrint(pointer(ptruint(s2) - sizeof(pointer))^), ptrint(pointer(ptruint(s2) - 2 * sizeof(pointer))^)]);
addlog('%0 %1 %2', [s2, pointer(s2), pointer(s2)] ); Код: Выделить всё
Win32/FPC 2.6.4:
(поток №0) 8 -1
(поток №0) phthagn! 144573F4h 144573F4h
(поток №0) 9 1
(поток №0) phthagn!# 14522070h 14522070h
(поток №0) --
(поток №0) 9 3
(поток №0) phthagn!ы 14522090h 14522090h
(поток №0) 10 1
(поток №0) phthagn!ы# 14522070h 14522070h
(поток №0) --
(поток №0) 1 2
(поток №0) 9 1451A068h 1451A068h
(поток №0) 2 1
(поток №0) 9# 1451A098h 1451A098h
Win64/FPC 2.6.4:
(поток №0) 8 -1
(поток №0) phthagn! 001101011D0h 001101011D0h
(поток №0) 9 1
(поток №0) phthagn!# 00006476BE0h 00006476BE0h
(поток №0) --
(поток №0) 9 3
(поток №0) phthagn!ы 00006476C20h 00006476C20h
(поток №0) 10 1
(поток №0) phthagn!ы# 00006476BE0h 00006476BE0h
(поток №0) --
(поток №0) 1 2
(поток №0) 9 000063F1B80h 000063F1B80h
(поток №0) 2 1
(поток №0) 9# 000063F1B20h 000063F1B20hЭто был бы полный.
>if Length(indices) <= 0 then Exit;
Читал (правда, в контексте PS3), что условия, которые в 99% случаев не сработают и при которых код всё равно отработает корректно, не нужны: так, проверка в «if size > 0 then Move(a, b, size)» лишняя. Мол, нужно писать код под наиболее типичный случай, а не граничные. То же касается, например, брейков по маловероятным условиям, когда без брейка всё равно отработает корректно и не сильно медленнее.
Читал (правда, в контексте PS3), что условия, которые в 99% случаев не сработают и при которых код всё равно отработает корректно, не нужны: так, проверка в «if size > 0 then Move(a, b, size)» лишняя. Мол, нужно писать код под наиболее типичный случай, а не граничные. То же касается, например, брейков по маловероятным условиям, когда без брейка всё равно отработает корректно и не сильно медленнее.
runewalsh писал(а):так, проверка в «if size > 0 then Move(a, b, size)» лишняя
зависит от Move и настроек компилятора.
например:
Код: Выделить всё
...const s : array of byte;
Move(s[0], b[0], length(s));
вызовет Range Check error, если проверка на Range включена, а length(s)=0.
по-этому для аккуратного паскалиста следует писать именно
Код: Выделить всё
...const s : array of byte;
...
if length(s)>0 then Move(s[0], b[0], length(s));
неаккуратный паскалист может писать так:
Код: Выделить всё
{$R-}
...const s : array of byte;
Move(s[0], b[0], length(s));
перфекционист напишет так:
Код: Выделить всё
{$ifndef release}
{$R+}
{$endif}
...const s : array of byte;
{$ifdef release}
Move(s[0], b[0], length(s));
{$else]
if length(s)>0 then Move(s[0], b[0], length(s));
{$endif}
ИМХО: с тех пор, как графика общитывается GPU, а не CPU, на санитарных проверках можно и не экономить.
ЗЫ. к слову об эффективности glVertex и массивах точек. Выяснилось, что ZenGL эмулирует immediate-режим OpenGL-а для OpenGLES 1.0 (в котором этого immediate режима не было изначально)
Добавлено спустя 1 минуту 58 секунд:
runewalsh писал(а): (правда, в контексте PS3)
PlayStation 3?
>вызовет Range Check error
Move(pointer(s)^, pointer(b)^, length(s)). В данном случае pointer(nil)^ легален со всеми проверками, т. к. разворачивается просто в nil.
>PlayStation 3?
https://macton.smugmug.com/Other/2008-0 ... Fi/n-xmKDH — он говорит про Load-Hit-Store и другие специфичные вещи, но вот тот же «statistically common case» я бы даже к преждевременной оптимизации не отнёс, скорее к здравому смыслу.
Move(pointer(s)^, pointer(b)^, length(s)). В данном случае pointer(nil)^ легален со всеми проверками, т. к. разворачивается просто в nil.
>PlayStation 3?
https://macton.smugmug.com/Other/2008-0 ... Fi/n-xmKDH — он говорит про Load-Hit-Store и другие специфичные вещи, но вот тот же «statistically common case» я бы даже к преждевременной оптимизации не отнёс, скорее к здравому смыслу.
runewalsh писал(а):Move(pointer(s)^, pointer(b)^, length(s)). В данном случае pointer(nil)^ легален со всеми проверками, т. к. разворачивается просто в nil.
две заметки:
1) (эстетическая) ну сам же ты так - с приведением к поинтерому - писать не станешь?!
2) (техническая) а я не зря написал const s: array of byte; намекая на то, что "s" может быть и открытым, а не динамическним массивом. С ним такое приведение типов просто не прокатит
Код: Выделить всё
procedure DoStuffOnBitmap( ... const s: array of byte ..)
Move(pointer(s)^, pointer(b)^, length(s))
Error: Illegal type conversion: "Open Array Of Byte" to "Pointer"
Ну и попытка сделать такое приведение
pointer(@s[0])^
вызовет всё тот же Range Check error
>if Length(indices) <= 0 then Exit;
Читал (правда, в контексте PS3), что условия, которые в 99% случаев не сработают и при которых код всё равно отработает корректно, не нужны:
Без этого ниже по течению словит AV, так как указатель на нулевой элемент массива скармливается glDrawElements()
Главная радость: запустилося!
А вот попытка откатить сессию не прерывая работы, вызвала красный экран смерти (который как синий экран смерти, только красный). Короче, основной поток бомбанул капитально, а поток игровой логики остался крутиться без руля и ветрил в бесконечном грузячем цикле. Однозначно что-то распидорасило в менеджере ассетов.
Основное надругательство над кодом заключалось в массовой замене <чегототам>.Scrape; на <чегототам>:= nil;
Ещё, 64-битная версия игрового модуля *почти* пошла, но Чеперси бычит на неверно рассчитанное выравнивание полей и размер инстансов.
Ещё, Vampyre Imaging в 64-битном варианте таки не читала жпеги. Вылечил, расковыряв ей дефайны, там вместо IMJPEGLIB надо было выбрать PASJPEG.
Cheb писал(а):там вместо IMJPEGLIB надо было выбрать PASJPEG
я б так не делал, PASJPEG на данный момент будет упорно проигрывать IMJPEGLIB (в скорости и поддержке jpeg-ов).
...ну если бы и делал, то как временное решение, с большим-большим TODO - использовать Си-шную либу.
то же самое касается zlib.
на данный момент будет упорно проигрывать IMJPEGLIB (в скорости и поддержке jpeg-ов).
В принципе, поддержка жпегов - это затычка для совместимости с openarena, сам в игре планирую использовать только png и tga.
то же самое касается zlib.
Всё своё ношу с собой (портировал своими руками на использование потоков): un_unzip.pp. Из стандартных юзает только zbase, в котором одни хидеры и объявления типов-записей.
Последний раз редактировалось Cheb 21.08.2019 08:48:35, всего редактировалось 1 раз.
