Как выводить спрайты быстрее?

Работа с 2D и 3D графикой, видео, звуком.

Модератор: Mikle

Dagobert
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 141
Зарегистрирован: 21.12.2002 (Сб) 6:48
Откуда: Russia

Как выводить спрайты быстрее?

Сообщение Dagobert » 24.07.2004 (Сб) 5:26

Я делаю 2D движок на DirectDraw7.
При простой прорисовке экрана 800х600х16 (пара блитов в бэк-буфер а затем флип) у меня выходит 3-4 флипа в секунду максимум. И объекты движутся рывками вместо плавного движения.
Машина :oops: :
Pentium 100MHz
32MB RAM
Видео - встроенное.
Вот такие я эксперименты ставлю.
Подскажите, где я ошибаюсь.

mad_Max
Бывалый
Бывалый
 
Сообщения: 203
Зарегистрирован: 15.09.2002 (Вс) 21:17
Откуда: Russia, Cherepovets

Сообщение mad_Max » 24.07.2004 (Сб) 22:27

Дык хоть бы кусочек исходника - инциализация как минимум. Может и машина виновата, но не уверен. Мой изометрический 2D-движок (DX7) работал на Pentium150Mhz, 32Mb RAM, 2 Mb S3Trio64v2 в режиме 800x600x16 с минимум 30fps, и делал он нечто посложнее пары блитов и флипа. Посмотри, в этой теме выложены его исходники и исполняемый файл.[/url]

Dagobert
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 141
Зарегистрирован: 21.12.2002 (Сб) 6:48
Откуда: Russia

Сообщение Dagobert » 26.07.2004 (Пн) 0:56

Посмотрел твой движок - обалдеть. Вот только на моей экспериментальной машине он не идёт. Пишет Automation Error (Это если при созданиии поверхностей использовать DDSCAPS_VIDEOMEMORY). Я не стал его перелопачивать.
В своей программе я не устанавливаю в какой памяти создавать поверхность, выбирается по умолчанию.
Кстати немножко кода:
Код: Выделить всё
Public Sub InitDirectDraw()
    On Local Error GoTo InitDirecctDRawError
    Dim MSDSD As DDSURFACEDESC2
    Dim BBDSC As DDSCAPS2
    Dim I As Long, sFName As String
    Set DX = New DirectX7
   
    Set DDraw = DX.DirectDrawCreate(vbNullString)
    DDraw.SetCooperativeLevel fSCreen.hWnd, DDSCL_EXCLUSIVE Or DDSCL_FULLSCREEN Or DDSCL_MULTITHREADED Or DDSCL_ALLOWMODEX
   
    DDraw.SetDisplayMode ScreenInfo.ResWidth, ScreenInfo.ResHeight, ScreenInfo.BPP, 0, DDSDM_DEFAULT
   
    With MSDSD
        .lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
        .ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_COMPLEX Or DDSCAPS_FLIP
        .lBackBufferCount = 1
        End With
   
    Set MainScreen = DDraw.CreateSurface(MSDSD)
    BBDSC.lCaps = DDSCAPS_BACKBUFFER
    Set BackBuffer = MainScreen.GetAttachedSurface(BBDSC)
   
        If ScreenInfo.BPP = 8 Then
        Set DDPalette = DDraw.LoadPaletteFromBitmap(GameInfo.RootPath & "pal.pal")
        If Not DDPalette Is Nothing Then
            MainScreen.SetPalette DDPalette
            End If
        End If
    For I = 0 To 1
        sFName = "tiles" & Format$(I, "0##")
        If Not LoadTileset(sFName & ".bmp", sFName & ".def", I, ScreenInfo.UseVideoMemory) Then LogEvent "Tileset Load Error: " & sFName: Quit
        Next I
    Exit Sub
InitDirecctDRawError:
    LogEvent "InitDD Error #" & Err.Number & "  " & Err.Description
    Quit
    End Sub

Код: Выделить всё
Public Sub RenderMapSurface(MapRect As DXvblib.RECT, Dest As DirectDrawSurface7)
    Dim I As Long, C As Long, ScrRect As DXvblib.RECT
    If MapRect.Top < 0 Then MapRect.Top = 0
    If MapRect.Left < 0 Then MapRect.Left = 0
    If MapRect.Right > Map.MapWidth Then MapRect.Right = Map.MapWidth
    If MapRect.Bottom > Map.MapHeight Then MapRect.Bottom = Map.MapHeight
    For I = MapRect.Left To MapRect.Right
        For C = MapRect.Top To MapRect.Bottom
            Call DrawSprite(Map.Items(I, C).TileID, Dest, (I - MapRect.Left) * Map.MapItemSize, (C - MapRect.Top) * Map.MapItemSize)
            Next C
        Next I
    End Sub

Код: Выделить всё
Public Sub DrawSprite(ByVal Index As Long, Dest As DirectDrawSurface7, ByVal X As Long, ByVal Y As Long)
    'On Error Resume Next
    Dim TSIdx As Long, TSSprIdx As Long
    Dim SprX As Long, SprY As Long, SprRect As DXvblib.RECT, SprRectShift As Long
    TSIdx = Index \ 256
    TSSprIdx = Index Mod 256
    SprX = X - TileSets(TSIdx).Tiles(TSSprIdx).CenterX
    SprY = Y - TileSets(TSIdx).Tiles(TSSprIdx).CenterY
    SprRect = TileSets(TSIdx).Tiles(TSSprIdx).SrcRect
    If TileSets(TSIdx).Tiles(TSSprIdx).AnimationCount > 0 Then
        With TileSets(TSIdx).Tiles(TSSprIdx)
            If (SpriteTimer - .AnimationTimer) > .AnimationSpeed Then
                .AnimationFrame = .AnimationFrame + 1
                .AnimationTimer = SpriteTimer
                If .AnimationFrame > .AnimationCount Then .AnimationFrame = 0
                End If
            SprRectShift = Abs(.SrcRect.Right - .SrcRect.Left) * .AnimationFrame
            SprRect.Left = SprRect.Left + SprRectShift
            SprRect.Right = SprRect.Right + SprRectShift
            End With
        End If
    Call Dest.BltFast(SprX, SprY, TileSets(TSIdx).SrcSurface, SprRect, DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY)
    End Sub

Может дашь какие-нибудь советы, или основные принципы.
Кстати забыл описать видео на моей машине - Chips & Technologies, 1MB.

mad_Max
Бывалый
Бывалый
 
Сообщения: 203
Зарегистрирован: 15.09.2002 (Вс) 21:17
Откуда: Russia, Cherepovets

Сообщение mad_Max » 26.07.2004 (Пн) 23:07

Ну с установкой типа памяти можешь не заморачиваться - в моем движке я все равно не везде обоснованно создавал Surface в VideoMemory (перелопачивать, кстати, совсем не обязательно - достаточно заменить во всем проекте строку "DDSCAPS_VIDEOMEMORY" на "0" с помощью Ctrl+H). Теперь касательно твоего кода. Инициализация вполне стандартна и придраться вроде не к чему (правда я не знаю назначения DDSCL_ALLOWMODEX, в моей проге он не используется). Больше вопросов вызывают две другие процедуры. В DrawSprite ты постоянно обращаешься к TileSets(TSIdx).Tiles(TSSprIdx), и использование блока With очень даже логично, но вот почему он использован только после трех подряд идущих обращений к вышеуказанному тайлу? Т.е не понятно, почему бы не поставить "With..." сразу после строки "TSSprIdx = Index Mod 256" и "End With" после "End If". В остальном вроде нормально. В процедуру RenderMapSurface я въехал не до конца. Я так понимаю, это действие происходит в цикле отрисовки и флипа. Не понимаю я параметра процедуры MapRect. Если этот Rect служит для обозначения области экрана, в которой происходит отрисовка спрайтов, то два цикла For при прорисовке изображения вызовут 800*600=480000 раз не очень быструю процедуру DrawSprite при соответствующем разрешении, что оччень неслабо. Если MapRect обозначает что-то другое, то я просто не врубился.

Dagobert
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 141
Зарегистрирован: 21.12.2002 (Сб) 6:48
Откуда: Russia

Сообщение Dagobert » 27.07.2004 (Вт) 1:25

Спасибо, что заметил With ... End With а то я этих тонкостей последнее время не замечаю. Кстати, Я слышал, что этот With тормозит когда используется с типами а не с классами. Что скажешь?
Насчёт MapRect - это кусок карты во внутренних координатах.

P.S. Любое изменение кода я считаю перелопачиванием.

mad_Max
Бывалый
Бывалый
 
Сообщения: 203
Зарегистрирован: 15.09.2002 (Вс) 21:17
Откуда: Russia, Cherepovets

Сообщение mad_Max » 27.07.2004 (Вт) 17:26

Про тормоза With применительно к пользовательским типам я никогда не слышал, поэтому провел небольшой тест. Создал свой тип с тремя integer полями и дважды прогнал For'ом по 99999999 итераций (при меньшем количестве результат слишком нестабилен), присваивая полям случайные значения от 0 до 999. With использовался только во втором случае. Результат: 38,706 секунд без With, 37,654 с With на Athlon XP 1800+, так что вроде даже быстрее, но вот если With... End With поставить внутри цикла, то результат на 4,12 секунды хуже, чем без With. Но учитывая количество итераций и приблизительность теста, разница не очень значительная, надо ещё поискать случай, когда использование With с пользовательскими типами способно заметно затормозить прогу. Как правило, главный тормоз в DirectDraw-приложении - сам Blit, поэтому их количество надо стараться минимизировать. Еще один тормоз - рисование линий и прочих геометрических фигур, может даже покруче BltFast будет. Как-то попытался в своем движке отобразить маршрут линиями, так количество флипов в секунду упало в 2 раза (на том же Athlon'е). Иногда очень непросто сразу определить, где именно в алгоритме "узкое" место, поэтому я рекомендую делать замеры времени на выполнение основных процедур (Timer или DX.TickCount помогут) и разбираться с самыми тормозными. Подсчитай кстати еще и количество блитов за один кадр - может где-то перебор. У меня при разрешении 800x600 в среднем 500-1200 блитов, в основном небольшие картинки 64x47, так что если все-таки запустишь мой движок, и он заработает с удовлетворительной скоростью, то можно будет ориентироваться на эти показатели. Если нет, то не знаю, что еще можно придумать. Я не имел возможности проверить свою прогу на машине мендленнее вышеупомянутого Pentium 150Mhz, но хоть 10 кадров в секунду должна же она выжать.

Dagobert
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 141
Зарегистрирован: 21.12.2002 (Сб) 6:48
Откуда: Russia

Сообщение Dagobert » 28.07.2004 (Ср) 2:14

Спасибо попробую

Dagobert
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 141
Зарегистрирован: 21.12.2002 (Сб) 6:48
Откуда: Russia

Сообщение Dagobert » 29.07.2004 (Чт) 11:11

Кстати, теперь (после добавления MULTITHREAD) выдаёт - 7FPS (Это на ММХ100) и 9FPS на ММХ100.
Замер показал, что на блит карты одним куском - 500х300 занимает 16мс, а группы спрайтов - меньше 1. Перерисовка всей видимой поверхности карты (кусочек - 50х50) - 32мс.
Подскажите, как мне уменьшить это число блитов. Я прорисовываю карту только когда что-то на ней изменилось, или сдвинулся вьюлорт. При выводе на экран, я копирую из буфера отрисованную карту, и по одному на неё всех видимых юнитов. Больше никаких действий. Ну разве что FPS - рисую текстом и всё.

mad_Max
Бывалый
Бывалый
 
Сообщения: 203
Зарегистрирован: 15.09.2002 (Вс) 21:17
Откуда: Russia, Cherepovets

Сообщение mad_Max » 29.07.2004 (Чт) 23:46

Странно, что при таких показателях такой низкий FPS. Может скинешь EXEшник потестить? Желательно чтобы он при нажатии какой-нибудь кнопки скидывал статистику в лог-файл (те же замеры времени, FPS, короче чем больше инфы - тем лучше).

Dagobert
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 141
Зарегистрирован: 21.12.2002 (Сб) 6:48
Откуда: Russia

Сообщение Dagobert » 13.08.2004 (Пт) 2:37

А ларчик просто открывался. Оказывается в условиях крайнего севера, т. е. на такой машине, DD не переваривает большие (как по размеру изображения, так и по объёму) BMP'шки. Стоило мне уменьшить файлик, и FPS увеличилось в 1,5 раза. И при этом я каждого юнита подписываю через DrawText, и DrawBox'ом рисую ему шкалу здоровья.


Вернуться в Мультимедиа

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 35

    TopList