2015-12-23 7 views
8

मैं एक कण इंजन लिख रहा हूं और देखा है कि यह होना चाहिए जितना धीमा होना चाहिए (मैंने अत्यधिक अन-अनुकूलित 3 डी सी ++ कण इंजन लिखे हैं जो 50 एफ कणों को 60 एफपीएस पर प्रस्तुत कर सकते हैं, यह लगभग 1.2 एफपीएस पर गिर जाता है 1.2 के ..), मैंने कणों के प्रतिपादन को मानते हुए कोड पर कुछ विश्लेषण किया था या घूर्णन सबसे अधिक CPU गहन ऑपरेशन थे, हालांकि मुझे पता चला कि वास्तव में ग्राफिक्स ऑब्जेक्ट के इन दो छोटे गुण वास्तव में 70% से अधिक खा रहे हैंग्राफिक्स। ट्रांसफॉर्म व्यापक रूप से अक्षम है, मैं इसके बारे में क्या कर सकता हूं?

g.Transform = m; 
g.Transform = m2; 
: मेरे प्रदर्शन ....

public void RotateParticle(Graphics g, RectangleF r, 
           RectangleF rShadow, float angle, 
           Pen particleColor, Pen particleShadow) 
    { 
     //Create a matrix 
     Matrix m = new Matrix(); 
     PointF shadowPoint = new PointF(rShadow.Left + (rShadow.Width/1), 
             rShadow.Top + (rShadow.Height/1)); 
     PointF particlePoint = new PointF(r.Left + (r.Width/1), 
              r.Top + (r.Height/2)); 
     //Angle of the shadow gets set to the angle of the particle, 
     //that way we can rotate them at the same rate 
     float shadowAngle = angle;     
     m.RotateAt(shadowAngle, shadowPoint); 

     g.Transform = m; 

     //rotate and draw the shadow of the Particle 
     g.DrawRectangle(particleShadow, rShadow.X, rShadow.Y, rShadow.Width, rShadow.Height); 

     //Reset the matrix for the next draw and dispose of the first matrix 
     //NOTE: Using one matrix for both the shadow and the partice causes one 
     //to rotate at half the speed of the other. 
     g.ResetTransform(); 
     m.Dispose(); 

     //Same stuff as before but for the actual particle 
     Matrix m2 = new Matrix(); 
     m2.RotateAt(angle, particlePoint); 

     //Set the current draw location to the rotated matrix point 
     //and draw the Particle 
     g.Transform = m2; 

     g.DrawRectangle(particleColor, r.X, r.Y, r.Width, r.Height); 
     m2.Dispose(); 
    } 

अपने प्रदर्शन क्या मार रहा है विशेष रूप से इन लाइनों है

एक छोटी सी पृष्ठभूमि, ग्राफिक्स ऑब्जेक्ट दर्द निवारक से पकड़ा जा रहा है, फिर यह रेंडर कण विधि में स्क्रीन पर कणों को प्रतिपादित कर रहा है, जो इस विधि को किसी भी घूर्णन करने के लिए कहते हैं, बहु-थ्रेडिंग ग्राफिक्स ऑब्जेक्ट के रूप में समाधान नहीं है एकाधिक धागे के बीच साझा नहीं किया जा सकता है।

https://gyazo.com/229cfad93b5b0e95891eccfbfd056020

मैं थोड़े इस सोच रहा हूँ कुछ है कि वास्तव में मदद नहीं किया जा सकता है, क्योंकि यह संपत्ति की तरह दिखता है: यहाँ कोड विश्लेषण मैं सिर्फ ताकि आप देख सकते हैं कि रूप में अच्छी तरह हो रहा है भाग गया के लिए एक लिंक है खुद प्रदर्शन को नष्ट कर रहा है और कुछ भी नहीं जो मैंने वास्तव में किया है (हालांकि मुझे यकीन है कि सुधार के लिए जगह है), खासकर जब से कक्षा में कॉल करने वाले डीएलएल सबसे सीपीयू पावर का उपयोग कर रहे हैं। वैसे भी, किसी भी मदद की इसे अनुकूलित करने की कोशिश में बहुत सराहना की जाएगी ... शायद मैं प्रदर्शन बढ़ाने के लिए रोटेशन को सक्षम/अक्षम कर दूंगा, हम देखेंगे ...

+0

इसके अलावा मैं छवि के लिए क्षमा चाहता हूं, मुझे पता है कि यह वास्तव में छोटा है, अगर आप झुकाते हैं तो आप संख्याओं को देख सकते हैं! : पी –

उत्तर

3

बनाने के लिए मुश्किल नहीं है खैर, आपको अपने प्रोफ़ाइल को देखने के कुछ समय बाद अपने सिर को खरोंच करना चाहिए। जब आप ट्रांसफॉर्म प्रॉपर्टी असाइन करते हैं तो अन्य कुछ चल रहा है। रीसेट ट्रान्सफॉर्म() को कुछ भी लागत नहीं होने पर आप कुछ कारण बता सकते हैं। निश्चित रूप से समझ में नहीं आता है, यह विधि ट्रांसफॉर्म संपत्ति को भी बदलती है।

और ध्यान दें कि यह DrawRectangle() होना चाहिए जो महंगी विधि होनी चाहिए क्योंकि वह वास्तव में धातु को पेडल डालता है और वास्तविक ड्राइंग आदेश उत्पन्न करता है। हम नहीं देख सकते कि यह आपके स्क्रीनशॉट से क्या खर्च करता है, 30% से अधिक नहीं हो सकता है। यह लगभग पर्याप्त नहीं है।

मुझे लगता है कि आप यहां जो देखते हैं वह जीडीआई/प्लस की एक अस्पष्ट विशेषता है, यह बैच ड्राइंग आदेश। दूसरे शब्दों में, आंतरिक रूप से यह ड्राइंग कमांड की एक सूची उत्पन्न करता है और उन्हें तब तक वीडियो ड्राइवर को पास नहीं करता है जब तक इसे करना न हो। मूल winapi में एक ऐसा फ़ंक्शन है जो स्पष्ट रूप से उस सूची को फ़्लश करने के लिए मजबूर करता है, यह GdiFlush() है। हालांकि यह .NET ग्राफिक्स वर्ग द्वारा खुलासा नहीं किया गया है, यह स्वचालित रूप से किया जाता है।

तो एक सुंदर आकर्षक सिद्धांत यह है कि जब आप ट्रांसफॉर्म संपत्ति आवंटित करते हैं तो जीडीआई + आंतरिक रूप से GdiFlush() को कॉल करता है। तो आप जो लागत देख रहे हैं वह वास्तव में पिछले DrawRectangle() कॉल की लागत है।

आपको इसे बैच करने का अधिक अवसर देकर आगे बढ़ने की आवश्यकता है। ग्राफिक्स क्लास विधि का बहुत दृढ़ता से समर्थन करते हैं जो आपको बड़ी संख्या में आइटम खींचने देता है। दूसरे शब्दों में, प्रत्येक व्यक्तिगत कण को ​​आकर्षित न करें बल्कि कई खींचें। आपको DrawRectangles(), DrawLines(), DrawPath() पसंद आएगा। दुर्भाग्य से कोई DrawPolygons(), जिसे आप वास्तव में पसंद करते हैं, तकनीकी रूप से आप PolyPolygon() को पिनवोक कर सकते हैं लेकिन यह जाना मुश्किल है।

यदि मेरा सिद्धांत गलत है तो ध्यान दें कि आपको ग्राफिक्स की आवश्यकता नहीं है। ट्रांसफॉर्म। आप Matrix.TransformPoints() और Graphics.DrawPolygon() का भी उपयोग कर सकते हैं। चाहे आप वास्तव में आगे बढ़ सकें, थोड़ा सा संदिग्ध है, ग्राफिक्स क्लास सीधे GPU त्वरण का उपयोग नहीं करता है, इसलिए यह डायरेक्टएक्स के साथ कभी भी प्रतिस्पर्धा नहीं करता है।

+0

ठोस उत्तर, जो समझ में आता है, और यह बताएगा कि प्रत्येक व्यक्ति को कॉल क्यों इतना गहन है। जब मैं काम से घर जाता हूं और आपको बताता हूं कि यह कैसे जाता है, तो मैं इसे देख लूंगा, बहुत बहुत धन्यवाद! –

3

मुझे यकीन नहीं है कि निम्नलिखित मदद करेगा , लेकिन यह कोशिश करने लायक है। इसके बजाय आवंटन/बताए/नई Matrix निपटान के, पूर्व आबंटित Graphics के माध्यम से Graphics.Transform तरीकों का उपयोग - RotateTransform, ScaleTransform, TranslateTransform (और हमेशा ResetTransform जब किया यह सुनिश्चित कर लें)।

GraphicsMatrix.RotateAt विधि का एक सीधा बराबर शामिल नहीं है, लेकिन यह एक

public static class GraphicsExtensions 
{ 
    public static void RotateTransformAt(this Graphics g, float angle, PointF point) 
    { 
     g.TranslateTransform(point.X, point.Y); 
     g.RotateTransform(angle); 
     g.TranslateTransform(-point.X, -point.Y); 
    } 
} 

तो फिर तुम इस तरह अपना कोड अपडेट और देख सकते हैं कि अगर मदद करता है

public void RotateParticle(Graphics g, RectangleF r, 
           RectangleF rShadow, float angle, 
           Pen particleColor, Pen particleShadow) 
{ 
    PointF shadowPoint = new PointF(rShadow.Left + (rShadow.Width/1), 
            rShadow.Top + (rShadow.Height/1)); 
    PointF particlePoint = new PointF(r.Left + (r.Width/1), 
             r.Top + (r.Height/2)); 
    //Angle of the shadow gets set to the angle of the particle, 
    //that way we can rotate them at the same rate 
    float shadowAngle = angle; 

    //rotate and draw the shadow of the Particle 
    g.RotateTransformAt(shadowAngle, shadowPoint); 
    g.DrawRectangle(particleShadow, rShadow.X, rShadow.Y, rShadow.Width, rShadow.Height); 
    g.ResetTransform(); 

    //Same stuff as before but for the actual particle 
    g.RotateTransformAt(angle, particlePoint); 
    g.DrawRectangle(particleColor, r.X, r.Y, r.Width, r.Height); 
    g.ResetTransform(); 
} 
+0

यह वास्तव में एफपीएस की मदद करता है, यह वास्तव में मदद नहीं करता है जब कण बड़े होते हैं, लेकिन जब वे छोटे होते हैं तो प्रदर्शन लाभ थोड़ा सा होता है, दुर्भाग्यवश यह अभी भी लगभग 1700 कणों पर 64 एफपीएस खो रहा है। इसके अलावा घूर्णन इस विधि के साथ बहुत अलग है, लेकिन एक अच्छे तरीके से, यह अच्छा लग रहा है :) –

1

क्या आप अपने कण को ​​आकर्षित करने के लिए ऑफ-स्क्रीन बफर बना सकते हैं, और OnPaint बस अपना ऑफ स्क्रीन बफर प्रस्तुत कर सकते हैं? आप समय-समय पर अपनी स्क्रीन को अद्यतन करने की जरूरत है, तो आप अपने परदे पर नियंत्रण/कैनवास को अमान्य कर सकता है, एक Timer

Bitmap bmp; 
Graphics gOff; 

void Initialize() { 
    bmp = new Bitmap(width, height); 
    gOff = bmp.FromImage(); 
} 

private void OnPaint(object sender, System.Windows.Forms.PaintEventArgs e) { 
    e.Graphics.DrawImage(bmp, 0, 0); 
} 

void RenderParticles() { 
    foreach (var particle in Particles) 
     RotateParticle(gOff, ...); 
} 


एक और नोट पर, किसी भी कारण का उपयोग कर कहते हैं एक मैट्रिक्स वस्तु हर बार जब आप RotateParticle फोन बनाने के लिए ? मैंने कोशिश नहीं की है, लेकिन एमएसडीएन दस्तावेज़ों का सुझाव है कि Graphics.Transform पर प्राप्त करें और सेट करें हमेशा एक प्रति बनायेगा। तो आप वर्ग स्तर पर Matrix ऑब्जेक्ट रख सकते हैं और इसे बदलने के लिए उपयोग कर सकते हैं। इसका उपयोग करने से पहले Matrix.Reset() पर कॉल करना सुनिश्चित करें। यह आपको कुछ प्रदर्शन सुधार प्राप्त कर सकता है।

संबंधित मुद्दे

 संबंधित मुद्दे