· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.A&O —› Функция InRect для графики

. 1 . 2 . 3 . >>

Посл.отвђт Сообщенiе


Дата: Янв 2, 2004 16:12:18

Всегда мечтал написать оптимизированную функцию In Rectangle. Уже сто раз их писал, но все както до оптимизации дело не доходило. А тут... С головой ушел в программирование под DirectX. Делаю систему окон для игры.
Там эта самая функция будет вызываться раз 100 в секунду.
Надо бы написать оптимизированный вариант под современные процессоры.

Задача такая: имеется прямоугольник с координатами (x,y),размерами (sx,sy) и координаты мышки (mx,my)
Обычно проверка такая (только для x):

if (mx>=x && mx<=x+sx) ...

но можно еще так:

a=mx-x;
if (a>=0 && a<=sx) ...

А как тоже самое написать на асме да еще с оптимизацией при условии, что функция должна возвращать 1 если мышка внутри прямоугольника и 0 в противном случае?


Дата: Янв 2, 2004 17:02:03 · Поправил: S_T_A_S_

Доброго времени суток
Я сейчас только начинаю погружаться с головой..

Возможно, мое мнение покажется странным.
Оно базируется на древнем опыте написания окошек на спектруме.


К сожалению, с терминологией у меня проблемы, попробую обьяснить на пальцах
Создаем окно - т.е. рисуем его на сурфэйсе. Параллельно с этим рисеум "ТЕНЬ" окна в другом буфере.
У каждого окна - свой "цвет" тени, т.е. разными байтами рисуем разные окна.
Если окна нет - то цвет тени будут, скажем, 0.
По нажатию мыши смотрим цвет тени. Все.
При этом можно иметь круглые/треугольные окна.

Можно в виде цвета тени использовать DWORD - адрес нужной процедуры. Тогда DWORD для фона будет являться поинтером на простой ret. Тут уж совсем без проверок :)

Минус - тратим время на рисование "тени".
Имхо, тратим меньше, чем приобретаем.

Тут много зависит от конкретной реализации. В вашем случае, возможно, оптимальным будет использовать по биту на пиксель (т.е. один "цвет"). А может быть alpha канал..

А проверки мне никогда не нравились :)
К тому же современные процессоры хорошо исполняют только предсказуемый код


Дата: Янв 2, 2004 17:58:14 · Поправил: Black_mirror

  mov eax,mx;if (mx>=x && mx<x+sx)
  sub eax,x
  cmp eax,sx
  sbb eax,eax;eax=-1 else eax=0
  mov edx,my;if (my>=y && my<y+sy)
  sub edx,y
  cmp edx,sy
  sbb edx,edx;edx=-1 else edx=0
  and eax,edx;eax=-1 если внутри и eax=0 если снаружи
  ret


Дата: Янв 2, 2004 18:46:02

Можно использовать API PtInRect или написать свою, например так:
InRect proc x:DWORD, y:DWORD, wdth:DWORD, hght:DWORD, mx:DWORD, my:DWORD

	mov eax, mx
	cmp eax, x
	jl OutOfRect	; Mouse.X < Rect.Left
	
	sub eax, x
	cmp wdth, eax
	jle OutOfRect	; Mouse.X > Rect.Right
	
	mov eax, my
	cmp eax, y
	jl OutOfRect	; Mouse.Y < Rect.Top
	
	sub eax, y
	cmp hght, eax
	jle OutOfRect	; Mouse.Y > Rect.Bottom
	
	mov eax, TRUE
	ret
OutOfRect:
	mov eax, FALSE
	ret
	
InRect endp


Дата: Янв 2, 2004 19:14:45

Black_mirror
Спасибо! То, что надо!!! Предсказуемый код! без переходов по условию. Может быть за такт выполнится :)
Сейчас попробую в сишке написать...

S_T_A_S_
А как ты тень хранишь? Сурфэйсом? В видюхе или в оперативке? Я тоже об этом думал, но чето мне както не нравится функция ReadPixel еще с древних времен VGA. Я кстати так и не смог ее на VGA написать.
Я думаю так: Сначала проверяем находится ли мышка на сурфэйсе, потом если это необходимо проверяем эту самую тень (если кнопка "кривая").
Если можешь дай ключевые куски кода или ссылку в инете почитать как эту тень сделать - пригодится.


Дата: Янв 2, 2004 19:33:59

Кстати а если числа - float то как?


Дата: Янв 2, 2004 20:45:28

Я протестил функцию с помошью RDTSC и вот результат:
вызов этой функции
bool GWnd::InRect()
{
return (input->x>=m_Rect.x && input->x<m_Rect.x+m_Rect.sx && input->y>=m_Rect.y && input->y<m_Rect.y+m_Rect.sy);
}

~1500 тактов

а этой
bool GWnd::InRect()
{
int x = m_Rect.x;
int y = m_Rect.y;
int sx = m_Rect.sx;
int sy = m_Rect.sy;
int mx = input->x;
int my = input->y;
__asm
{
mov eax,mx
sub eax,x
cmp eax,sx
sbb eax,eax
mov edx,my
sub edx,y
cmp edx,sy
sbb edx,edx
and eax,edx
}
return;
}

~3000 тактов

Тоесть примерно в 2 раза медленнее. Я проверил - это из-за преобразования типов - они занимают примерно 2000 тактов.


Дата: Янв 2, 2004 21:02:45

Куски кода для Z80? Дя они у меня на 5'25 флопах :)
Я же это столет назад делал. Теперь вот опять начал.

Сурфейс (я под этим понимаю весь экран) естественно в оперативке. Из видеопамяти чтение идет ОЧЕНЬ медленно.
Потом копирую BltFast на бакбуфер и затем flip.

Сначала проверяем находится ли мышка на сурфэйсе, потом если это необходимо проверяем эту самую тень
Представим, что у нас только два цвета. Черный - фон. Белый - окна.
Теперь можно просто определить, есть ли окно под мышью: посмотреть цвет пикселя.
Но цветов-то на самом деле больше, поэтому ReadPixel не подходит. Поэтому и создается "черно-белая копия" экрана. С ней то проще работать, чем непосредственно с экраном.

Это самый простой вариант. Он может лишь показать есть окно или нет.
А если использовати не 1 бит на пиксель а DWORD, то можно сильно упростить логику работы программы. Например кнопку "закрыть окно" рисуем цветом "offset CloseWindow". Верхнюю часть окна - "offset MoveWindow".
Перед этим очищаем буфер "цветом" "offset DoNothing"

Теперь как просто обрабатывать нажатия мыши! Никаких проверок.
Просто JMP Shadow[X*Y]

Хотя рисование на экране несколько усложняется. Т.к. надо два раза рисовать.
Но и тут есть несколько вариантов.
Например можно использовать 32bpp и для наших целей использовать Alpha байт. Здесь красивый jmp уже не получится, надо будет табличку использовать.
Если рисовать "ручками" (а я так и делаю), то можно просто записывать сразу в два места в памяти, т.е. скорость кода практически не изменится.

Но вы немного опередили меня со своим вопросом.
Я это еще не реализовал пока. Еще не решил в каком виде у меня окна будут. Это важный момент.

Хотя.. подобный прием я использовал в новогодней деме.
Там "снег" прилипает. У меня 2888 снежинок. Представляете, если бы были проверки?



Про range-test (первый вопрос) можно еще посмотреть вот здесь
Хорошие примеры.


Дата: Янв 2, 2004 21:10:37

Кстати а если числа - float то как?
Лучше использовать числа с фиксированной точкой. Например младшие 8 бит - дробная часть


Дата: Янв 2, 2004 21:21:45

А кто нибудь может написать ту же проверку только для float ато я сопроцессор не очень знаю.
Декомпилил то, что написал на си - ох и тупой же всетаки компилятор :-) Условный переход на каждой проверке а всего команд гдето 30-50.
Всем спасибо за ответы!


Дата: Янв 2, 2004 21:29:01

S_T_A_S_
А что за фиксированная точка - много раз слышал, но ни разу не видел. И как ее можно применить в директе - там же все вертексы - float векторы?


Дата: Янв 2, 2004 23:18:31

И пусть будут float. Это для рендеринга на экран.
Для ваших личных функций можно любые форматы использовать.
Хотя мне сложно судить о вашей программе, т.к. я не знаю как вы окошки рисуете.
Много окошек? Если много то любые InRect будут неэффективны, имхо.
Особенно если будут перекрывающиеся окошки.
Честно сказать, мне тоже окошки придется рисовать скоро и я не вижу других путей, кроме моего старого.


Фиксированная запятая. Пусть есть два dword: X и Y.
Обычно рисуем точку с координатами (X;Y). А можно с координатами, например, (X/256;Y/256).
Т.е. получается, что для вывода на экран младший байт как-бы не нужен.
Зато его можно использовать для "плавного" изменения координаты. Например увеличиваем на 2 каждый кадр. Реально же на экране точка сдвинется через 128 кадров.


Дата: Янв 2, 2004 23:28:14

Для float примерно так:
sub esp,16
fld [mx]
fsub [x];mx-x
fst dword [esp];<0(bit31=1) если не попадает 
fsub [sx];(mx-x)-sx=mx-(x+sx)
fstp dword [esp+4];>0(bit31=0) если не попадает
fld [my]
fsub [y];my-y
fst dword [esp+8];<0(bit31=1) если не попадает
fsub [sy];my-(y+sy)
fstp dword [esp+12];>0(bit31=0) если не попадает
mov eax,[esp]
or eax,[esp+8];bit31=0 если не попадает
not eax
and eax,[esp+4]
and eax,[esp+12]
shr eax,31;eax=1 если попадает и eax=0 если не попадает
add esp,16


Дата: Янв 3, 2004 07:18:53

S_T_A_S_
А операция деления? Ну ладно сдвиг на 8 вправо если я не ошибаюсь занимает по 2 такта на 1 бит. И это окупается? Вроде бы современные процессоры с float работают ничуть не хуже, чем с DWORD. Правда с преобразованием типов проблемы.
Я както писал РГР по программированию в универ - игра арканоид. Так вот в ней я все сделал на целочисленной арифметике. А мой одногрупник написал арканоида на float так у него плавность движения мячика была куда лучше, чем у меня. А скорость не пострадала.

На счет оконной системы - у меня дерево. В корне пустое окно, от которого расходятся ветки. Активно используется наследование и полиморфизм. События - указатели на функции типа OnClick и тп. Так что много окон опрашиваться не будут.

Black_mirror
Сейчас попробую...


Дата: Янв 3, 2004 10:09:36 · Поправил: profi_r

Протестил - ~2500 тактов. Все равно медленно...
bool GWnd::InRect()
{
	float x = m_Rect.x;
	float y = m_Rect.y;
	float sx = m_Rect.sx;
	float sy = m_Rect.sy;
	float mx = input->x;
	float my = input->y;
	int a,b,c,d;
	__asm
	{
		fld		mx
		fsub	x
		fist	a
		fsub	sx
		fistp	b
		fld		my
		fsub	y
		fist	c
		fsub	sy
		fistp	d
		mov		eax,a
		or		eax,c
		not		eax
		and		eax,b
		and		eax,d
		rol		eax,1
	}
	return;
}

. 1 . 2 . 3 . >>


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.058