Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dxDrawModel3D reimplementation #3266

Merged
merged 16 commits into from
Sep 3, 2024

Conversation

tederis
Copy link
Member

@tederis tederis commented Dec 9, 2023

Brings dxDrawModel3D function(#3172) back. The syntax is compatible with the previous one: bool dxDrawModel3D(int model, float x, float y, float z, float rx, float ry, float rz, [float sx = 1.0f, float sy = 1.0f, float sz = 1.0f).

@tederis
Copy link
Member Author

tederis commented Dec 9, 2023

The mode argument is debatable. Maybe it's better to replace it with , bool blocking = false argument? What do you think?

@TheNormalnij
Copy link
Member

I feel draw functions shouldn't manage model loading. Using this draw function with blocking mode is totally wrong, it causes FPS drops. Async mode is bad too, it causes drawing in a wrong frame. So only "loaded" mode is good here.
I think we need two additional functions for this:

bool engineLoadModel( number modelId, [ bool async = true, function callback)
bool engineUnloadModel( number modelId )

@tederis
Copy link
Member Author

tederis commented Dec 9, 2023

I feel draw functions shouldn't manage model loading. Using this draw function with blocking mode is totally wrong, it causes FPS drops. Async mode is bad too, it causes drawing in a wrong frame. So only "loaded" mode is good here. I think we need two additional functions for this:

bool engineLoadModel( number modelId, [ bool async = true, function callback)
bool engineUnloadModel( number modelId )

I feel the same. But the nonblocking model loading is exactly what SA streamer is doing. I mean this is natural for SA.

engineLoadModel is good but the fact is there is no way to preserve a model in memory without a refs count increment. I'm not sure if preserving a model in memory by force is something good.

engineUnloadModel raises an another question. What if a model exists on a map when this function is called? Should we remove it as well? Or we should decrement a refs count?

Looks like these functions do not fit that easy in the context of dxDrawModel3D.

@PlatinMTA
Copy link
Contributor

Are the rendering issues (models becoming invisible at certain view-angles for example) fixed in this PR?

@tederis
Copy link
Member Author

tederis commented Dec 9, 2023

Are the rendering issues (models becoming invisible at certain view-angles for example) fixed in this PR?

Of course. This PR is written from scratch keeping in mind all problems of the previous one. Moreover, it would be weird to just reopen a PR with the same problems.

@TheNormalnij
Copy link
Member

engineLoadModel is good but the fact is there is no way to preserve a model in memory without a refs count increment. I'm not sure if preserving a model in memory by force is something good.

engineUnloadModel raises an another question. What if a model exists on a map when this function is called? Should we remove it as well? Or we should decrement a refs count?

engineLoadModel and engineUnoadModel are bad names.
First function should increase refs count and load model.
Second function should decrease refs count and unload model when refs count is 0.

@tederis
Copy link
Member Author

tederis commented Dec 9, 2023

engineLoadModel is good but the fact is there is no way to preserve a model in memory without a refs count increment. I'm not sure if preserving a model in memory by force is something good.
engineUnloadModel raises an another question. What if a model exists on a map when this function is called? Should we remove it as well? Or we should decrement a refs count?

engineLoadModel and engineUnoadModel are bad names. First function should increase refs count and load model. Second function should decrease refs count and unload model when refs count is 0.

Okay, I'll think about it.

@Pirulax
Copy link
Contributor

Pirulax commented Dec 12, 2023

I think such a function (that loads models) could be handy in other situations too (like pre-loading a section of the world for custom maps, etc).
Also, in theory it's possible to add support for rendering a CClientDFF directly no?

@StrixG StrixG added the enhancement New feature or request label Dec 21, 2023
@Pirulax
Copy link
Contributor

Pirulax commented Dec 27, 2023

Hey, any updates on this?

@tederis
Copy link
Member Author

tederis commented Dec 27, 2023

Hey, any updates on this?

Yes, I aggregated all suggestions so that the final PR will contain the following:
dxDrawModel3D function that works with preloaded only models (or CClientDFF elements)
engineLoadModel function
engineUnloadModel function

It requires some polishing and the internal testing. I hope to finish it soon.

@Pirulax
Copy link
Contributor

Pirulax commented Dec 29, 2023

Though, by using SA naming, engineStreamingRequestModel and engineStreamingRemoveModel (Or Unload?) might be more suitable?|, not sure, might be confusing, as in reality we're just adding/removing refs.
I'm worried people might get confused as to what the difference between engineLoadDFF and engineLoadModel is

@tederis
Copy link
Member Author

tederis commented Dec 31, 2023

Though, by using SA naming, engineStreamingRequestModel and engineStreamingRemoveModel (Or Unload?) might be more suitable?|, not sure, might be confusing, as in reality we're just adding/removing refs. I'm worried people might get confused as to what the difference between engineLoadDFF and engineLoadModel is

I thought about it too. And I have come up with the following syntax:
bool engineStreamingRequestModel(uint model [, bool async = false, bool addRef = false]) -- Note that each resource can keep just one reference for each model.

bool engineStreamingReleaseModel(uint model) -- Removes the reference if it was previously incremented by engineStreamingRequestModel.

string engineStreamingGetModelState(uint model) -- Return "unavailable", "loading" or "loaded".

There also can be event "onClientModelLoaded". But I'm not sure if this event is actually needed.

@TheNormalnij
Copy link
Member

TheNormalnij commented Dec 31, 2023 via email

@tederis
Copy link
Member Author

tederis commented Dec 31, 2023

How will it work with addRef = false and addRef = true ?

The better name for this argument is "retain". It increments the ref count for the specific model. But only one additional reference per resource. When addRef = false it's just trying to load a model without any guarantees. It's the way SA is doing the streaming. addRef = false is convinient for the case when you need a model just in the current(async = false) or the next frame(async = true).

@TheNormalnij
Copy link
Member

TheNormalnij commented Dec 31, 2023 via email

@tederis
Copy link
Member Author

tederis commented Dec 31, 2023

"the next frame" I remember gta uses some sort of multi threading for file loading. Does gta guarantee that the model will be loaded exactly on the next frame?

No, even SA does not guarantee that. I previously mentioned the next frame to emphasize the asynchronous nature of the argument. Of course the precise moment isn't specified.

@tederis
Copy link
Member Author

tederis commented Dec 31, 2023

engineStreamingRequestModel with addRef = false can be considered as "dear function, please try to load a model. But if it fails it's OK, at least you tried."

addRef = true is more aggressive and will try to load a model until the success(if the model is valid of course), plus adds a reference.

@tederis
Copy link
Member Author

tederis commented Dec 31, 2023

I think it's even better to replace addRef = false with persistent = false. It's more illustrative.

@Fernando-A-Rocha
Copy link
Contributor

@tederis It is viable to have this feature now, right?

@tederis
Copy link
Member Author

tederis commented May 31, 2024

@tederis It is viable to have this feature now, right?

There could be graphical issues with translucent objects but aside of it, it's viable. There were also proposals that complicate the entire system but they aren't essential. I will try to find time to give it another look.

@tederis tederis force-pushed the dx_draw_model_3d branch from 3d6e600 to 1ef20fc Compare June 1, 2024 09:34
@lynconsix
Copy link

Will there still be any updates? I hope very much, as this function could be very useful

@tederis
Copy link
Member Author

tederis commented Jul 25, 2024

This PR was initially small and simple. During code reviews several suggestions was made that complicate the initial idea. These suggestions may look minor but hide many edge cases that require thorough approach. In this regard I propose to separate dxDrawModel3D function from engineStreamingRequestModel/engineStreamingReleaseModel functions which will be addressed in the dedicated PR. This will close the breach in MTA Lua API and make this PR simpler to finish and merge it. What do you think @TheNormalnij ?

@TheNormalnij
Copy link
Member

I'll assist you with engineStreamingRequestModel and engineStreamingReleaseModel.

@lynconsix
Copy link

Great, this pull will be of great importance.

@Dutchman101
Copy link
Member

@tederis Please resolve conflicts.
Also, with PR #3611 now merged, what else needs to be done, or is it about ready?

@lynconsix
Copy link

In my opinion, @Dutchman101, @tederis commented that the problem was the 3D model allocator, with that already resolved by PR #3611 we can comment that now we only need to resolve some parts.

@tederis
Copy link
Member Author

tederis commented Aug 15, 2024

@tederis Please resolve conflicts. Also, with PR #3611 now merged, what else needs to be done, or is it about ready?

Done. I have also added a render path for transparent objects. So this PR is fully functional now. But there is still a question whether this function should always work with "loaded" models or there can be several modes(the current implementation) [See the second comment by @TheNormalnij]. My personal opinion is that there's nothing wrong with it and can be considered as a "sugar" for the convenience.

@TheNormalnij
Copy link
Member

I suggest to remove modes completely, because they have unpredictable behavior (the function doesn't guarantee render) and cause fps drops (in blocking mode) and model flickering (all modes).
This function can guarantee predictable result in this example:

local modelId = 1337

local function drawMyModel()
    dxDrawModel3D(modelId, 0, 0, 4, 0, 0, 0)
end

local function startDraw()
    engineStreamingRequestModel(modelId, true, true)
    addEventHandler("onClientPreRender", root, drawMyModel)
end

local function stopDraw()
    engineStreamingReleaseModel(modelId, true)
    removeEventHandler("onClientPreRender", root, drawMyModel)
end

@Dutchman101 Dutchman101 added the feedback Further information is requested label Aug 18, 2024
@tederis
Copy link
Member Author

tederis commented Aug 22, 2024

I suggest to remove modes completely, because they have unpredictable behavior (the function doesn't guarantee render) and cause fps drops (in blocking mode) and model flickering (all modes). This function can guarantee predictable result in this example:

local modelId = 1337

local function drawMyModel()
    dxDrawModel3D(modelId, 0, 0, 4, 0, 0, 0)
end

local function startDraw()
    engineStreamingRequestModel(modelId, true, true)
    addEventHandler("onClientPreRender", root, drawMyModel)
end

local function stopDraw()
    engineStreamingReleaseModel(modelId, true)
    removeEventHandler("onClientPreRender", root, drawMyModel)
end

This function is indeed doesn't guarantee render. But why do we need that? Async loading is what GTA renderer doing every frame. The blocking loading mode can be considered as an 3D analogue of dxDrawImage(with an image path as an argument). And now, when we have engineStreamingRequestModel/engineStreamingReleaseModel functions a developer can choose between them and a loading mode. The only change that I would make is set default mode to loaded.

@TheNormalnij
Copy link
Member

The blocking loading mode can be considered as an 3D analogue of dxDrawImage(with an image path as an argument).

dxDrawImage causes only one fps drop on first use. dxDrawModel3D causes fps drops randomly.

This function is indeed doesn't guarantee render. But why do we need that?

It's about API quality. I think that predictable functions are better than functions with uncontrollable result. mode parameter looks like we recommend to use this function with async and blocking modes. I can't recommend these modes except specific cases.

It's still possible to use this function without modes when you are ready for bugs:

local modelId = 1337

engineStreamingRequestModel(modelId, false, false)
dxDrawModel3D(modelId, 0, 0, 4, 0, 0, 0)

-- or
engineStreamingRequestModel(modelId, false, true)
dxDrawModel3D(modelId, 0, 0, 4, 0, 0, 0)

@tederis
Copy link
Member Author

tederis commented Aug 25, 2024

Your point is fair enough and I'm inclined to accept it. Okay, since there's no other's opinions I'll make the changes.

@Fernando-A-Rocha
Copy link
Contributor

The streaming model request functions have been implemented. This is also ready? 😇

@TheNormalnij
Copy link
Member

The streaming model request functions have been implemented. This is also ready? 😇

Yeah, but our build server doesn't work. I'm afraid to put too many changes in a single build

@TheNormalnij TheNormalnij merged commit 04ef14b into multitheftauto:master Sep 3, 2024
6 checks passed
@tederis tederis deleted the dx_draw_model_3d branch January 10, 2025 12:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feedback Further information is requested
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants